Merge "DO NOT MERGE - Merge Android 10 into master"
diff --git a/.gitignore b/.gitignore
index 7240e48..694b40c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,4 +13,5 @@
 local.properties
 gradle/
 build/
-gradlew*
\ No newline at end of file
+gradlew*
+.DS_Store
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..4c38205
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,32 @@
+// 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.
+
+android_library {
+    name: "launcher-aosp-tapl",
+    static_libs: [
+        "androidx.annotation_annotation",
+        "androidx.test.runner",
+        "androidx.test.rules",
+        "androidx.test.uiautomator_uiautomator",
+        "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",
+    ],
+    manifest: "tests/tapl/AndroidManifest.xml",
+    platform_apis: true,
+}
diff --git a/Android.mk b/Android.mk
index f5ec7cd..9d113d9 100644
--- a/Android.mk
+++ b/Android.mk
@@ -17,16 +17,27 @@
 LOCAL_PATH := $(call my-dir)
 
 #
-# Prebuilt Java Libraries
+# Build rule for plugin lib (needed to write a plugin).
 #
 include $(CLEAR_VARS)
-LOCAL_MODULE := libSharedSystemUI
+LOCAL_USE_AAPT2 := true
+LOCAL_AAPT2_ONLY := true
 LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-LOCAL_SRC_FILES := quickstep/libs/sysui_shared.jar
-LOCAL_UNINSTALLABLE_MODULE := true
+
+ifneq (,$(wildcard frameworks/base))
+    LOCAL_STATIC_JAVA_LIBRARIES:= PluginCoreLib
+else
+    LOCAL_STATIC_JAVA_LIBRARIES:= libPluginCore
+endif
+
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under, src_plugins)
+
 LOCAL_SDK_VERSION := current
-include $(BUILD_PREBUILT)
+LOCAL_MIN_SDK_VERSION := 28
+LOCAL_MODULE := LauncherPluginLib
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
 
 #
 # Build rule for Launcher3 dependencies lib.
@@ -37,13 +48,17 @@
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_STATIC_ANDROID_LIBRARIES := \
-    android-support-v4 \
-    android-support-v7-recyclerview \
-    android-support-dynamic-animation
+    androidx.recyclerview_recyclerview \
+    androidx.dynamicanimation_dynamicanimation \
+    androidx.preference_preference \
+    iconloader_base
+
+LOCAL_STATIC_JAVA_LIBRARIES := LauncherPluginLib
 
 LOCAL_SRC_FILES := \
     $(call all-proto-files-under, protos) \
-    $(call all-proto-files-under, proto_overrides)
+    $(call all-proto-files-under, proto_overrides) \
+    $(call all-java-files-under, src_build_config) \
 
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 
@@ -68,18 +83,24 @@
 LOCAL_USE_AAPT2 := true
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_STATIC_ANDROID_LIBRARIES := Launcher3CommonDepsLib
+LOCAL_STATIC_ANDROID_LIBRARIES := \
+    Launcher3CommonDepsLib \
+    SecondaryDisplayLauncherLib
 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, src_flags)
 
 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_PRODUCT_MODULE := true
 LOCAL_OVERRIDES_PACKAGES := Home Launcher2
 LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
 
@@ -100,7 +121,7 @@
 LOCAL_SRC_FILES := \
     $(call all-java-files-under, src) \
     $(call all-java-files-under, src_ui_overrides) \
-    $(call all-java-files-under, go/src_flags)
+    $(call all-java-files-under, go/src)
 
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/go/res
 
@@ -110,6 +131,7 @@
 LOCAL_MIN_SDK_VERSION := 21
 LOCAL_PACKAGE_NAME := Launcher3Go
 LOCAL_PRIVILEGED_MODULE := true
+LOCAL_PRODUCT_MODULE := true
 LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3QuickStep
 LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
 
@@ -129,21 +151,32 @@
 LOCAL_AAPT2_ONLY := true
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_STATIC_JAVA_LIBRARIES := libSharedSystemUI
-LOCAL_STATIC_ANDROID_LIBRARIES := Launcher3CommonDepsLib
+ifneq (,$(wildcard frameworks/base))
+  LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLib launcherprotosnano
+  LOCAL_PRIVATE_PLATFORM_APIS := true
+else
+  LOCAL_STATIC_JAVA_LIBRARIES := libSharedSystemUI libLauncherProtos
+  LOCAL_SDK_VERSION := system_current
+  LOCAL_MIN_SDK_VERSION := 26
+endif
+LOCAL_MODULE := Launcher3QuickStepLib
+LOCAL_PRIVILEGED_MODULE := true
+LOCAL_STATIC_ANDROID_LIBRARIES := \
+    Launcher3CommonDepsLib \
+    SecondaryDisplayLauncherLib
 
 LOCAL_SRC_FILES := \
     $(call all-java-files-under, src) \
     $(call all-java-files-under, quickstep/src) \
-    $(call all-java-files-under, src_flags)
+    $(call all-java-files-under, quickstep/recents_ui_overrides/src) \
+    $(call all-java-files-under, src_flags) \
+    $(call all-java-files-under, src_shortcuts_overrides)
 
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/quickstep/res
+LOCAL_RESOURCE_DIR := \
+    $(LOCAL_PATH)/quickstep/res \
+    $(LOCAL_PATH)/quickstep/recents_ui_overrides/res
 LOCAL_PROGUARD_ENABLED := disabled
 
-LOCAL_SDK_VERSION := system_current
-LOCAL_MIN_SDK_VERSION := 26
-LOCAL_MODULE := Launcher3QuickStepLib
-LOCAL_PRIVILEGED_MODULE := true
 
 LOCAL_MANIFEST_FILE := quickstep/AndroidManifest.xml
 include $(BUILD_STATIC_JAVA_LIBRARY)
@@ -158,15 +191,21 @@
 LOCAL_STATIC_ANDROID_LIBRARIES := Launcher3QuickStepLib
 LOCAL_PROGUARD_ENABLED := disabled
 
-LOCAL_SDK_VERSION := system_current
-LOCAL_MIN_SDK_VERSION := 26
+ifneq (,$(wildcard frameworks/base))
+  LOCAL_PRIVATE_PLATFORM_APIS := true
+else
+  LOCAL_SDK_VERSION := system_current
+  LOCAL_MIN_SDK_VERSION := 26
+endif
 LOCAL_PACKAGE_NAME := Launcher3QuickStep
 LOCAL_PRIVILEGED_MODULE := true
 LOCAL_PRODUCT_MODULE := true
 LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3
 LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
 
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/quickstep/res
+LOCAL_RESOURCE_DIR := \
+    $(LOCAL_PATH)/quickstep/res \
+    $(LOCAL_PATH)/quickstep/recents_ui_overrides/res
 
 LOCAL_FULL_LIBS_MANIFEST_FILES := \
     $(LOCAL_PATH)/AndroidManifest.xml \
@@ -185,25 +224,81 @@
 LOCAL_USE_AAPT2 := true
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_STATIC_JAVA_LIBRARIES := libSharedSystemUI
+ifneq (,$(wildcard frameworks/base))
+  LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLib launcherprotosnano
+  LOCAL_PRIVATE_PLATFORM_APIS := true
+else
+  LOCAL_STATIC_JAVA_LIBRARIES := libSharedSystemUI libLauncherProtos
+  LOCAL_SDK_VERSION := system_current
+  LOCAL_MIN_SDK_VERSION := 26
+endif
 LOCAL_STATIC_ANDROID_LIBRARIES := Launcher3CommonDepsLib
 
 LOCAL_SRC_FILES := \
     $(call all-java-files-under, src) \
     $(call all-java-files-under, quickstep/src) \
-    $(call all-java-files-under, go/src_flags)
+    $(call all-java-files-under, quickstep/recents_ui_overrides/src) \
+    $(call all-java-files-under, go/src)
 
 LOCAL_RESOURCE_DIR := \
     $(LOCAL_PATH)/quickstep/res \
+    $(LOCAL_PATH)/quickstep/recents_ui_overrides/res \
     $(LOCAL_PATH)/go/res
 
-LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+LOCAL_PROGUARD_ENABLED := full
 
-LOCAL_SDK_VERSION := system_current
-LOCAL_MIN_SDK_VERSION := 26
 LOCAL_PACKAGE_NAME := Launcher3QuickStepGo
 LOCAL_PRIVILEGED_MODULE := true
-LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3QuickStep
+LOCAL_PRODUCT_MODULE := true
+LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3QuickStep Launcher3GoIconRecents
+LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
+
+LOCAL_FULL_LIBS_MANIFEST_FILES := \
+    $(LOCAL_PATH)/go/AndroidManifest.xml \
+    $(LOCAL_PATH)/AndroidManifest.xml \
+    $(LOCAL_PATH)/AndroidManifest-common.xml
+
+LOCAL_MANIFEST_FILE := quickstep/AndroidManifest.xml
+LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.launcher3.*
+include $(BUILD_PACKAGE)
+
+#
+# Build rule for Launcher3 Go app with quickstep and Go-specific
+# version of recents for Android Go devices.
+#
+include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
+LOCAL_MODULE_TAGS := optional
+
+ifneq (,$(wildcard frameworks/base))
+  LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLib launcherprotosnano
+  LOCAL_PRIVATE_PLATFORM_APIS := true
+else
+  LOCAL_STATIC_JAVA_LIBRARIES := libSharedSystemUI libLauncherProtos
+  LOCAL_SDK_VERSION := system_current
+  LOCAL_MIN_SDK_VERSION := 26
+endif
+LOCAL_STATIC_ANDROID_LIBRARIES := Launcher3CommonDepsLib
+
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under, src) \
+    $(call all-java-files-under, quickstep/src) \
+    $(call all-java-files-under, go/src) \
+    $(call all-java-files-under, go/quickstep/src)
+
+LOCAL_RESOURCE_DIR := \
+    $(LOCAL_PATH)/quickstep/res \
+    $(LOCAL_PATH)/go/res \
+    $(LOCAL_PATH)/go/quickstep/res
+
+LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+LOCAL_PROGUARD_ENABLED := full
+
+LOCAL_PACKAGE_NAME := Launcher3GoIconRecents
+LOCAL_PRIVILEGED_MODULE := true
+LOCAL_PRODUCT_MODULE := true
+LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3Go Launcher3QuickStep
 LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
 
 LOCAL_FULL_LIBS_MANIFEST_FILES := \
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index 211e1ff..5318a12 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -44,6 +44,28 @@
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
     <uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
 
+
+    <!--
+    Permissions required for read/write access to the workspace data. These permission name
+    should not conflict with that defined in other apps, as such an app should embed its package
+    name in the permissions. eq com.mypackage.permission.READ_SETTINGS
+    -->
+    <permission
+        android:name="${packageName}.permission.READ_SETTINGS"
+        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+        android:protectionLevel="signatureOrSystem"
+        android:label="@string/permlab_read_settings"
+        android:description="@string/permdesc_read_settings"/>
+    <permission
+        android:name="${packageName}.permission.WRITE_SETTINGS"
+        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+        android:protectionLevel="signatureOrSystem"
+        android:label="@string/permlab_write_settings"
+        android:description="@string/permdesc_write_settings"/>
+
+    <uses-permission android:name="${packageName}.permission.READ_SETTINGS" />
+    <uses-permission android:name="${packageName}.permission.WRITE_SETTINGS" />
+
     <application
         android:backupAgent="com.android.launcher3.LauncherBackupAgent"
         android:fullBackupOnly="true"
@@ -88,8 +110,8 @@
 
         <service
             android:name="com.android.launcher3.notification.NotificationListener"
-            android:label="@string/icon_badging_service_title"
-            android:enabled="@bool/notification_badging_enabled"
+            android:label="@string/notification_dots_service_title"
+            android:enabled="@bool/notification_dots_enabled"
             android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
             <intent-filter>
                 <action android:name="android.service.notification.NotificationListenerService" />
@@ -100,7 +122,7 @@
                        android:value="true" />
 
         <activity android:name="com.android.launcher3.dragndrop.AddItemActivity"
-            android:theme="@android:style/Theme.DeviceDefault.Light.Dialog.Alert"
+            android:theme="@style/AppItemActivityTheme"
             android:excludeFromRecents="true"
             android:autoRemoveFromRecents="true"
             android:label="@string/action_add_to_workspace" >
@@ -118,5 +140,48 @@
             android:name="com.android.launcher3.launcher_dump_provider"
             android:value="com.android.launcher3.LauncherProvider" />
 
+        <!--
+        The settings provider contains Home's data, like the workspace favorites. The permissions
+        should be changed to what is defined above. The authorities should also be changed to
+        represent the package name.
+        -->
+        <provider
+            android:name="com.android.launcher3.LauncherProvider"
+            android:authorities="${packageName}.settings"
+            android:exported="true"
+            android:writePermission="${packageName}.permission.WRITE_SETTINGS"
+            android:readPermission="${packageName}.permission.READ_SETTINGS" />
+
+        <!--
+        The content provider for exposing various launcher grid options.
+        TODO: Add proper permissions
+        -->
+        <provider
+            android:name="com.android.launcher3.graphics.GridOptionsProvider"
+            android:authorities="${packageName}.grid_control"
+            android:enabled="false"
+            android:exported="true" />
+
+        <!--
+        The settings activity. To extend point settings_fragment_name to appropriate fragment class
+        -->
+        <activity
+            android:name="com.android.launcher3.settings.SettingsActivity"
+            android:label="@string/settings_button_text"
+            android:theme="@android:style/Theme.DeviceDefault.Settings"
+            android:autoRemoveFromRecents="true">
+            <intent-filter>
+                <action android:name="android.intent.action.APPLICATION_PREFERENCES" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
+        <provider
+            android:name="com.android.launcher3.testing.TestInformationProvider"
+            android:authorities="${packageName}.TestInfo"
+            android:readPermission="android.permission.WRITE_SECURE_SETTINGS"
+            android:writePermission="android.permission.WRITE_SECURE_SETTINGS"
+            android:exported="true"
+            android:enabled="false" />
     </application>
 </manifest>
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 3212980..b031ffb 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -20,35 +20,12 @@
 <manifest
     xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.launcher3">
-    <uses-sdk android:targetSdkVersion="23" android:minSdkVersion="21"/>
+    <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.
     -->
 
-    <!--
-    Permissions required for read/write access to the workspace data. These permission name
-    should not conflict with that defined in other apps, as such an app should embed its package
-    name in the permissions. eq com.mypackage.permission.READ_SETTINGS
-    -->
-    <permission
-        android:name="com.android.launcher3.permission.READ_SETTINGS"
-        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
-        android:protectionLevel="signatureOrSystem"
-        android:label="@string/permlab_read_settings"
-        android:description="@string/permdesc_read_settings"/>
-    <permission
-        android:name="com.android.launcher3.permission.WRITE_SETTINGS"
-        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
-        android:protectionLevel="signatureOrSystem"
-        android:label="@string/permlab_write_settings"
-        android:description="@string/permdesc_write_settings"/>
-
-    <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
-    <uses-permission android:name="com.android.launcher.permission.WRITE_SETTINGS" />
-    <uses-permission android:name="com.android.launcher3.permission.READ_SETTINGS" />
-    <uses-permission android:name="com.android.launcher3.permission.WRITE_SETTINGS" />
-
     <application
         android:backupAgent="com.android.launcher3.LauncherBackupAgent"
         android:fullBackupOnly="true"
@@ -84,33 +61,10 @@
                 <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>
 
-        <!--
-        The settings activity. When extending keep the intent filter present
-        -->
-        <activity
-            android:name="com.android.launcher3.SettingsActivity"
-            android:label="@string/settings_button_text"
-            android:theme="@android:style/Theme.DeviceDefault.Settings"
-            android:autoRemoveFromRecents="true">
-            <intent-filter>
-                <action android:name="android.intent.action.APPLICATION_PREFERENCES" />
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
-        </activity>
-
-        <!--
-        The settings provider contains Home's data, like the workspace favorites. The permissions
-        should be changed to what is defined above. The authorities should also be changed to
-        represent the package name.
-        -->
-        <provider
-            android:name="com.android.launcher3.LauncherProvider"
-            android:authorities="com.android.launcher3.settings"
-            android:exported="true"
-            android:writePermission="com.android.launcher3.permission.WRITE_SETTINGS"
-            android:readPermission="com.android.launcher3.permission.READ_SETTINGS" />
-
     </application>
 </manifest>
diff --git a/OWNERS b/OWNERS
index a8c84eb..6c1273f 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,4 +1,12 @@
+# Use this reviewer by default.
+# gnl-eng@google.com (Googlers only)
+
+# People who can approve changes for submission
+#
+
 adamcohen@google.com
 hyunyoungs@google.com
+mrcasey@google.com
 sunnygoyal@google.com
+twickham@google.com
 winsonc@google.com
diff --git a/SecondaryDisplayLauncher/Android.mk b/SecondaryDisplayLauncher/Android.mk
new file mode 100644
index 0000000..7f305bb
--- /dev/null
+++ b/SecondaryDisplayLauncher/Android.mk
@@ -0,0 +1,39 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
+LOCAL_AAPT2_ONLY := true
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_STATIC_ANDROID_LIBRARIES := com.google.android.material_material
+
+LOCAL_STATIC_JAVA_LIBRARIES := LauncherPluginLib
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 21
+LOCAL_MODULE := SecondaryDisplayLauncherLib
+LOCAL_PRIVILEGED_MODULE := true
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/SecondaryDisplayLauncher/AndroidManifest.xml b/SecondaryDisplayLauncher/AndroidManifest.xml
new file mode 100644
index 0000000..ebf6b02
--- /dev/null
+++ b/SecondaryDisplayLauncher/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<manifest
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.launcher3">
+
+    <application>
+
+        <activity
+            android:name="com.android.launcher3.SecondaryDisplayLauncher"
+            android:theme="@style/SecondaryLauncherTheme"
+            android:launchMode="singleTop"
+            android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density"
+            android:enabled="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SECONDARY_HOME" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
+    </application>
+</manifest>
diff --git a/SecondaryDisplayLauncher/res/drawable/ic_apps.xml b/SecondaryDisplayLauncher/res/drawable/ic_apps.xml
new file mode 100644
index 0000000..db779c2
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/drawable/ic_apps.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<vector android:height="24dp" android:tint="#FFFFFF"
+        android:viewportHeight="24.0" android:viewportWidth="24.0"
+        android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FF000000" android:pathData="M4,8h4L8,4L4,4v4zM10,20h4v-4h-4v4zM4,20h4v-4L4,16v4zM4,14h4v-4L4,10v4zM10,14h4v-4h-4v4zM16,4v4h4L20,4h-4zM10,8h4L14,4h-4v4zM16,14h4v-4h-4v4zM16,20h4v-4h-4v4z"/>
+</vector>
diff --git a/SecondaryDisplayLauncher/res/drawable/ic_settings.xml b/SecondaryDisplayLauncher/res/drawable/ic_settings.xml
new file mode 100644
index 0000000..c269c3b
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/drawable/ic_settings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<vector android:height="24dp" android:tint="#FFFFFF"
+        android:viewportHeight="24.0" android:viewportWidth="24.0"
+        android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FF000000" android:pathData="M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98s-0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.12,-0.22 -0.39,-0.3 -0.61,-0.22l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.23,-0.09 -0.49,0 -0.61,0.22l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98s0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.12,0.22 0.39,0.3 0.61,0.22l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.23,0.09 0.49,0 0.61,-0.22l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM12,15.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5 3.5,1.57 3.5,3.5 -1.57,3.5 -3.5,3.5z"/>
+</vector>
diff --git a/SecondaryDisplayLauncher/res/layout-sw600dp/secondary_display_launcher.xml b/SecondaryDisplayLauncher/res/layout-sw600dp/secondary_display_launcher.xml
new file mode 100644
index 0000000..46f1674
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/layout-sw600dp/secondary_display_launcher.xml
@@ -0,0 +1,56 @@
+<?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.
+  -->
+
+<androidx.coordinatorlayout.widget.CoordinatorLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/RootView"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@color/launcher_bg_color"
+    android:fitsSystemWindows="true" >
+
+    <GridView
+        android:id="@+id/pinned_app_grid"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_marginTop="@dimen/app_grid_margin_top"
+        android:layout_marginStart="@dimen/app_grid_margin_left"
+        android:layout_marginEnd="@dimen/app_grid_margin_right"
+        android:columnWidth="@dimen/app_list_col_width"
+        android:verticalSpacing="@dimen/app_list_horizontal_spacing"
+        android:horizontalSpacing="@dimen/app_list_vertical_spacing"
+        android:numColumns="auto_fit" />
+
+    <ImageButton
+        android:id="@+id/OptionsButton"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="bottom|start"
+        android:layout_marginStart="@dimen/options_button_margin"
+        android:layout_marginBottom="@dimen/options_button_margin"
+        android:src="@drawable/ic_settings"
+        android:background="@null"/>
+
+    <FrameLayout
+        android:layout_width="@dimen/app_picker_width"
+        android:layout_height="@dimen/app_picker_height"
+        android:layout_gravity="bottom|end"
+        android:layout_margin="@dimen/app_picker_fab_margin">
+
+        <include layout="@layout/app_picker_layout"/>
+    </FrameLayout>
+</androidx.coordinatorlayout.widget.CoordinatorLayout>
diff --git a/SecondaryDisplayLauncher/res/layout-sw720dp/secondary_display_launcher.xml b/SecondaryDisplayLauncher/res/layout-sw720dp/secondary_display_launcher.xml
new file mode 100644
index 0000000..6653a77
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/layout-sw720dp/secondary_display_launcher.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<androidx.coordinatorlayout.widget.CoordinatorLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/RootView"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@color/launcher_bg_color"
+    android:fitsSystemWindows="true" >
+
+    <GridView
+        android:id="@+id/pinned_app_grid"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_marginTop="@dimen/app_grid_margin_top"
+        android:layout_marginStart="@dimen/app_grid_margin_left"
+        android:layout_marginEnd="@dimen/app_grid_margin_right"
+        android:columnWidth="@dimen/app_list_col_width"
+        android:verticalSpacing="@dimen/app_list_horizontal_spacing"
+        android:horizontalSpacing="@dimen/app_list_vertical_spacing"
+        android:numColumns="auto_fit" />
+
+    <ImageButton
+        android:id="@+id/OptionsButton"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="bottom|start"
+        android:layout_marginStart="@dimen/options_button_margin"
+        android:layout_marginBottom="@dimen/options_button_margin"
+        android:src="@drawable/ic_settings"
+        android:background="@null"/>
+
+    <FrameLayout
+        android:layout_width="@dimen/app_picker_width"
+        android:layout_height="@dimen/app_picker_height"
+        android:layout_gravity="bottom|end"
+        android:layout_marginEnd="@dimen/app_picker_fab_margin"
+        android:layout_marginBottom="@dimen/app_picker_fab_margin">
+
+        <include layout="@layout/app_picker_layout"/>
+    </FrameLayout>
+</androidx.coordinatorlayout.widget.CoordinatorLayout>
diff --git a/SecondaryDisplayLauncher/res/layout/app_grid_item.xml b/SecondaryDisplayLauncher/res/layout/app_grid_item.xml
new file mode 100644
index 0000000..ee5158a
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/layout/app_grid_item.xml
@@ -0,0 +1,35 @@
+<?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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:gravity="center">
+
+    <ImageView
+        android:id="@+id/app_icon"
+        android:layout_width="@dimen/app_icon_width"
+        android:layout_height="@dimen/app_icon_height" />
+
+    <TextView
+        android:id="@+id/app_name"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:maxLines="1" />
+</LinearLayout>
diff --git a/SecondaryDisplayLauncher/res/layout/app_picker_dialog.xml b/SecondaryDisplayLauncher/res/layout/app_picker_dialog.xml
new file mode 100644
index 0000000..563a1af
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/layout/app_picker_dialog.xml
@@ -0,0 +1,34 @@
+<?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.
+  -->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <GridView
+        android:id="@+id/picker_app_grid"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_marginTop="@dimen/app_grid_margin_top"
+        android:layout_marginStart="@dimen/app_grid_margin_left"
+        android:layout_marginEnd="@dimen/app_grid_margin_right"
+        android:columnWidth="@dimen/app_list_col_width"
+        android:verticalSpacing="@dimen/app_list_horizontal_spacing"
+        android:horizontalSpacing="@dimen/app_list_vertical_spacing"
+        android:numColumns="auto_fit" />
+</FrameLayout>
diff --git a/SecondaryDisplayLauncher/res/layout/app_picker_layout.xml b/SecondaryDisplayLauncher/res/layout/app_picker_layout.xml
new file mode 100644
index 0000000..20f85b1
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/layout/app_picker_layout.xml
@@ -0,0 +1,46 @@
+<?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.
+  -->
+
+<androidx.coordinatorlayout.widget.CoordinatorLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <com.google.android.material.circularreveal.cardview.CircularRevealCardView
+        android:id="@+id/FloatingSheet"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:visibility="invisible">
+
+        <GridView
+            android:id="@+id/app_grid"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_marginTop="@dimen/app_grid_margin_top"
+            android:columnWidth="@dimen/app_list_col_width"
+            android:verticalSpacing="@dimen/app_list_horizontal_spacing"
+            android:horizontalSpacing="@dimen/app_list_vertical_spacing"
+            android:numColumns="auto_fit" />
+    </com.google.android.material.circularreveal.cardview.CircularRevealCardView>
+
+    <com.google.android.material.floatingactionbutton.FloatingActionButton
+        android:id="@+id/FloatingActionButton"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="bottom|end"
+        android:src="@drawable/ic_apps"/>
+</androidx.coordinatorlayout.widget.CoordinatorLayout>
diff --git a/SecondaryDisplayLauncher/res/layout/secondary_display_launcher.xml b/SecondaryDisplayLauncher/res/layout/secondary_display_launcher.xml
new file mode 100644
index 0000000..49cd499
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/layout/secondary_display_launcher.xml
@@ -0,0 +1,56 @@
+<?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.
+  -->
+
+<androidx.coordinatorlayout.widget.CoordinatorLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/RootView"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@color/launcher_bg_color"
+    android:fitsSystemWindows="true" >
+
+    <GridView
+        android:id="@+id/pinned_app_grid"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_marginTop="@dimen/app_grid_margin_top"
+        android:layout_marginStart="@dimen/app_grid_margin_left"
+        android:layout_marginEnd="@dimen/app_grid_margin_right"
+        android:columnWidth="@dimen/app_list_col_width"
+        android:verticalSpacing="@dimen/app_list_horizontal_spacing"
+        android:horizontalSpacing="@dimen/app_list_vertical_spacing"
+        android:numColumns="auto_fit" />
+
+    <ImageButton
+        android:id="@+id/OptionsButton"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="bottom|start"
+        android:layout_marginStart="@dimen/options_button_margin"
+        android:layout_marginBottom="@dimen/options_button_margin"
+        android:src="@drawable/ic_settings"
+        android:background="@null"/>
+
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_gravity="bottom|end"
+        android:layout_margin="@dimen/app_picker_fab_margin">
+
+        <include layout="@layout/app_picker_layout"/>
+    </FrameLayout>
+</androidx.coordinatorlayout.widget.CoordinatorLayout>
diff --git a/SecondaryDisplayLauncher/res/menu/context_menu.xml b/SecondaryDisplayLauncher/res/menu/context_menu.xml
new file mode 100644
index 0000000..6263842
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/menu/context_menu.xml
@@ -0,0 +1,23 @@
+<?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.
+  -->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/add_app_shortcut"
+          android:title="@string/add_app_shortcut" />
+    <item android:id="@+id/set_wallpaper"
+          android:title="@string/set_wallpaper" />
+</menu>
diff --git a/SecondaryDisplayLauncher/res/values-af/strings.xml b/SecondaryDisplayLauncher/res/values-af/strings.xml
new file mode 100644
index 0000000..b544be7a
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-af/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Kon nie die aktiwiteit begin nie"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Voeg programkortpad by"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Stel muurpapier"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-am/strings.xml b/SecondaryDisplayLauncher/res/values-am/strings.xml
new file mode 100644
index 0000000..71854ad
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-am/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"እንቅስቃሴውን ማስጀመር አልተቻለም"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"የመተግበሪያ አቋራጭ ያክሉ"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"ልጣፍ አዘጋጅ"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-ar/strings.xml b/SecondaryDisplayLauncher/res/values-ar/strings.xml
new file mode 100644
index 0000000..ba81c11
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-ar/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"تعذَّر تشغيل النشاط."</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"إضافة اختصار التطبيق"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"تعيين الخلفية"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-as/strings.xml b/SecondaryDisplayLauncher/res/values-as/strings.xml
new file mode 100644
index 0000000..d199a26
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-as/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"কাৰ্যকলাপটো লঞ্চ কৰিব পৰা নগ’ল"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"এপৰ শ্বর্টকাট যোগ কৰক"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"ৱালপেপাৰ ছেট কৰক"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-az/strings.xml b/SecondaryDisplayLauncher/res/values-az/strings.xml
new file mode 100644
index 0000000..cee70a0
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-az/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Fəaliyyəti başlatmaq mümkün olmadı"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Tətbiq qısayolu əlavə edin"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Divar kağızı ayarlayın"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-b+sr+Latn/strings.xml b/SecondaryDisplayLauncher/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..a8859d9
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Pokretanje aktivnosti nije uspelo"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Dodaj prečicu za aplikaciju"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Podesite pozadinu"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-be/strings.xml b/SecondaryDisplayLauncher/res/values-be/strings.xml
new file mode 100644
index 0000000..3df3760
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-be/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Не ўдалося запусціць дзеянне"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Дадаць ярлык праграмы"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Устанавіць шпалеры"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-bg/strings.xml b/SecondaryDisplayLauncher/res/values-bg/strings.xml
new file mode 100644
index 0000000..4474815
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-bg/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Активността не можа да бъде стартирана"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Добавяне на пряк път към приложението"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Задаване на тапет"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-bn/strings.xml b/SecondaryDisplayLauncher/res/values-bn/strings.xml
new file mode 100644
index 0000000..7322691
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-bn/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"অ্যাক্টিভিটি চালু করা যায়নি"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"অ্যাপ শর্টকাট যোগ করুন"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"ওয়ালপেপার সেট করুন"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-bs/strings.xml b/SecondaryDisplayLauncher/res/values-bs/strings.xml
new file mode 100644
index 0000000..1e59d33
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-bs/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Pokretanje aktivnosti nije uspjelo"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Dodaj prečicu aplikacije"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Postavi pozadinsku sliku"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-ca/strings.xml b/SecondaryDisplayLauncher/res/values-ca/strings.xml
new file mode 100644
index 0000000..c0274d1
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-ca/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"No s\'ha pogut iniciar l\'activitat"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Afegeix una drecera d\'aplicació"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Estableix el fons de pantalla"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-cs/strings.xml b/SecondaryDisplayLauncher/res/values-cs/strings.xml
new file mode 100644
index 0000000..92ed5fa
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-cs/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Aktivitu nelze zahájit"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Přidat zkratku aplikace"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Nastavení tapety"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-da/strings.xml b/SecondaryDisplayLauncher/res/values-da/strings.xml
new file mode 100644
index 0000000..16bdb33
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-da/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Aktiviteten kunne ikke startes"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Tilføj appgenvej"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Angiv baggrund"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-de/strings.xml b/SecondaryDisplayLauncher/res/values-de/strings.xml
new file mode 100644
index 0000000..3617a5b
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-de/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Aktivität konnte nicht gestartet werden"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"App-Verknüpfung hinzufügen"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Hintergrund festlegen"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-el/strings.xml b/SecondaryDisplayLauncher/res/values-el/strings.xml
new file mode 100644
index 0000000..8d19d09
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-el/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Δεν ήταν δυνατή η εκκίνηση της δραστηριότητας"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Προσθήκη συντόμευσης εφαρμογής"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Ορισμός ταπετσαρίας"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-en-rAU/strings.xml b/SecondaryDisplayLauncher/res/values-en-rAU/strings.xml
new file mode 100644
index 0000000..8d8c419
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-en-rAU/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Couldn\'t launch the activity"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Add app shortcut"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Set wallpaper"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-en-rGB/strings.xml b/SecondaryDisplayLauncher/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..8d8c419
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-en-rGB/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Couldn\'t launch the activity"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Add app shortcut"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Set wallpaper"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-en-rIN/strings.xml b/SecondaryDisplayLauncher/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..8d8c419
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-en-rIN/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Couldn\'t launch the activity"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Add app shortcut"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Set wallpaper"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-es-rUS/strings.xml b/SecondaryDisplayLauncher/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..ff6772b
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-es-rUS/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"No se pudo iniciar la actividad"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Agregar acceso directo a app"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Establecer fondo de pantalla"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-es/strings.xml b/SecondaryDisplayLauncher/res/values-es/strings.xml
new file mode 100644
index 0000000..0654dcb
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-es/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"No se ha podido iniciar la acción"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Añadir acceso directo a la aplicación"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Establecer fondo de pantalla"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-et/strings.xml b/SecondaryDisplayLauncher/res/values-et/strings.xml
new file mode 100644
index 0000000..3410fd4
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-et/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Tegevust ei saanud käivitada"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Lisa rakenduse otsetee"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Määra taustapilt"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-eu/strings.xml b/SecondaryDisplayLauncher/res/values-eu/strings.xml
new file mode 100644
index 0000000..d7abe33
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-eu/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Ezin izan da abiarazi jarduera"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Gehitu aplikaziorako lasterbidea"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Ezarri horma-papera"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-fa/strings.xml b/SecondaryDisplayLauncher/res/values-fa/strings.xml
new file mode 100644
index 0000000..4d3ec4d
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-fa/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"فعالیت راه‌اندازی نشد"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"افزودن میان‌بر برنامه"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"تنظیم کاغذدیواری"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-fi/strings.xml b/SecondaryDisplayLauncher/res/values-fi/strings.xml
new file mode 100644
index 0000000..e56f67a
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-fi/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Käynnistäminen epäonnistui"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Lisää sovelluksen pikakuvake"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Aseta taustakuva"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-fr-rCA/strings.xml b/SecondaryDisplayLauncher/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..f5c9ba5
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-fr-rCA/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Impossible de lancer l\'activité"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Ajouter un raccourci vers l\'application"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Définir le fond d\'écran"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-fr/strings.xml b/SecondaryDisplayLauncher/res/values-fr/strings.xml
new file mode 100644
index 0000000..daa186b
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-fr/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Impossible de lancer l\'activité"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Ajouter un raccourci vers l\'application"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Configurer le fond d\'écran"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-gl/strings.xml b/SecondaryDisplayLauncher/res/values-gl/strings.xml
new file mode 100644
index 0000000..0bcf969
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-gl/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Non se puido iniciar a actividade"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Engadir atallo da aplicación"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Definir fondo de pantalla"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-gu/strings.xml b/SecondaryDisplayLauncher/res/values-gu/strings.xml
new file mode 100644
index 0000000..82b4444
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-gu/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"પ્રવૃત્તિ લૉન્ચ કરી શકાઈ નથી"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"ઍપ શૉર્ટકટ ઉમેરો"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"વૉલપેપર સેટ કરો"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-hi/strings.xml b/SecondaryDisplayLauncher/res/values-hi/strings.xml
new file mode 100644
index 0000000..8adb519
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-hi/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"गतिविधि लॉन्च नहीं हो सकी"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"ऐप्लिकेशन शॉर्टकट जोड़ें"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"वॉलपेपर सेट करें"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-hr/strings.xml b/SecondaryDisplayLauncher/res/values-hr/strings.xml
new file mode 100644
index 0000000..87ac874
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-hr/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Pokretanje aktivnosti nije uspjelo"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Dodajte aplikacijski prečac"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Postavljanje pozadine"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-hu/strings.xml b/SecondaryDisplayLauncher/res/values-hu/strings.xml
new file mode 100644
index 0000000..a8870fc
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-hu/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Nem sikerült elindítani a tevékenységet"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Alkalmazás parancsikonjának hozzáadása"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Háttérkép beállítása"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-hy/strings.xml b/SecondaryDisplayLauncher/res/values-hy/strings.xml
new file mode 100644
index 0000000..a64233f
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-hy/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Չհաջողվեց գործարկել գործողությունը"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Ավելացնել հավելվածի դյուրանցումը"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Դարձնել պաստառ"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-in/strings.xml b/SecondaryDisplayLauncher/res/values-in/strings.xml
new file mode 100644
index 0000000..f51d238
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-in/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Tidak dapat meluncurkan aktivitas"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Tambahkan pintasan app"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Setel wallpaper"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-is/strings.xml b/SecondaryDisplayLauncher/res/values-is/strings.xml
new file mode 100644
index 0000000..e8b3e97
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-is/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Ekki tókst að ræsa aðgerðina"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Bæta við flýtileið forrita"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Velja veggfóður"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-it/strings.xml b/SecondaryDisplayLauncher/res/values-it/strings.xml
new file mode 100644
index 0000000..4941515
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-it/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Impossibile avviare l\'attività"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Aggiungi scorciatoia app"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Imposta sfondo"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-iw/strings.xml b/SecondaryDisplayLauncher/res/values-iw/strings.xml
new file mode 100644
index 0000000..06b0c42
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-iw/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"לא ניתן היה להפעיל את הפעילות"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"הוספת קיצור דרך של אפליקציה"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"הגדרת טפט"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-ja/strings.xml b/SecondaryDisplayLauncher/res/values-ja/strings.xml
new file mode 100644
index 0000000..3ed7b2b
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-ja/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"アクティビティを開始できませんでした"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"アプリのショートカットを追加"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"壁紙を設定"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-ka/strings.xml b/SecondaryDisplayLauncher/res/values-ka/strings.xml
new file mode 100644
index 0000000..ac85f70
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-ka/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"აქტივობის გაშვება ვერ მოხერხდა"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"აპის მალსახმობის დამატება"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"ფონის დაყენება"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-kk/strings.xml b/SecondaryDisplayLauncher/res/values-kk/strings.xml
new file mode 100644
index 0000000..f9ac455
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-kk/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Әрекет іске қосылмады"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Қолданба таңбашасын енгізу"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Тұсқағаз орнату"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-km/strings.xml b/SecondaryDisplayLauncher/res/values-km/strings.xml
new file mode 100644
index 0000000..afc050f
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-km/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"មិនអាចចាប់ផ្តើមសកម្មភាពទេ"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"បញ្ចូល​ផ្លូវកាត់​កម្មវិធី"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"កំណត់​ផ្ទាំង​រូបភាព"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-kn/strings.xml b/SecondaryDisplayLauncher/res/values-kn/strings.xml
new file mode 100644
index 0000000..09c327f
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-kn/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"ಚಟುವಟಿಕೆಯನ್ನು ಲಾಂಚ್‌ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"ಆ್ಯಪ್‌ ಶಾರ್ಟ್‌ಕಟ್ ಸೇರಿಸಿ"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"ವಾಲ್‌ಪೇಪರ್ ಹೊಂದಿಸಿ"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-ko/strings.xml b/SecondaryDisplayLauncher/res/values-ko/strings.xml
new file mode 100644
index 0000000..6a02ac0
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-ko/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"활동을 실행할 수 없음"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"앱 바로가기 추가"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"배경화면 설정"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-ky/strings.xml b/SecondaryDisplayLauncher/res/values-ky/strings.xml
new file mode 100644
index 0000000..56185fa
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-ky/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Аракет аткарылган жок"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Колдонмого кыска жол кошуу"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Тушкагаз орнотуу"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-lo/strings.xml b/SecondaryDisplayLauncher/res/values-lo/strings.xml
new file mode 100644
index 0000000..36a6275
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-lo/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"ບໍ່ສາມາດເປີດໃຊ້ການເຄື່ອນໄຫວໄດ້"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"ເພີ່ມທາງລັດແອັບ"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"ຕັ້ງເປັນຮູບພື້ນຫຼັງ"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-lt/strings.xml b/SecondaryDisplayLauncher/res/values-lt/strings.xml
new file mode 100644
index 0000000..8113eb6
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-lt/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Nepavyko paleisti veiklos"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Pridėti programos šaukinį"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Nustatyti ekrano foną"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-lv/strings.xml b/SecondaryDisplayLauncher/res/values-lv/strings.xml
new file mode 100644
index 0000000..e267933
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-lv/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Nevarēja palaist darbību"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Pievienot lietotnes saīsni"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Iestatīt fona tapeti"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-mk/strings.xml b/SecondaryDisplayLauncher/res/values-mk/strings.xml
new file mode 100644
index 0000000..e2cca03
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-mk/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Не можеше да се стартува активноста"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Додајте кратенка за апликација"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Поставете го тапетот"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-ml/strings.xml b/SecondaryDisplayLauncher/res/values-ml/strings.xml
new file mode 100644
index 0000000..864245b
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-ml/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"ആക്‌റ്റിവിറ്റി പ്രകാശിപ്പിക്കാനായില്ല"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"ആപ്പ് കുറുക്കുവഴികൾ ചേർക്കുക"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"വാൾപേപ്പർ സജ്ജീകരിക്കുക"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-mn/strings.xml b/SecondaryDisplayLauncher/res/values-mn/strings.xml
new file mode 100644
index 0000000..85fb020
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-mn/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Үйл ажиллагааг эхлүүж чадсангүй"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Аппын товчлол нэмэх"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Ханын зураг тохируулах"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-mr/strings.xml b/SecondaryDisplayLauncher/res/values-mr/strings.xml
new file mode 100644
index 0000000..6e92a2f
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-mr/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"अ‍ॅक्टिव्हिटी लाँच करता आली नाही"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"अ‍ॅप शॉर्टकट जोडा"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"वॉलपेपर सेट करा"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-ms/strings.xml b/SecondaryDisplayLauncher/res/values-ms/strings.xml
new file mode 100644
index 0000000..fd78053
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-ms/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Tidak dapat melancarkan aktiviti"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Tambah pintasan apl"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Tetapkan kertas dinding"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-my/strings.xml b/SecondaryDisplayLauncher/res/values-my/strings.xml
new file mode 100644
index 0000000..1521402
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-my/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"လုပ်ဆောင်ချက်ကို စတင်၍မရပါ"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"အက်ပ်ဖြတ်လမ်းလင့်ခ်ထည့်ရန်"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"နောက်ခံ သတ်မှတ်ရန်"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-nb/strings.xml b/SecondaryDisplayLauncher/res/values-nb/strings.xml
new file mode 100644
index 0000000..945c87b
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-nb/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Kunne ikke starte aktiviteten"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Legg til en appsnarvei"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Angi bakgrunn"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-ne/strings.xml b/SecondaryDisplayLauncher/res/values-ne/strings.xml
new file mode 100644
index 0000000..9a5b0a0
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-ne/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"उक्त क्रियाकलाप सुरु गर्न सकिएन"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"अनुप्रयोगको सर्टकट थप्नुहोस्‌"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"वालपेपर सेट गर्नुहोस्"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-nl/strings.xml b/SecondaryDisplayLauncher/res/values-nl/strings.xml
new file mode 100644
index 0000000..8767708
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-nl/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Kan de activiteit niet starten"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"App-snelkoppeling toevoegen"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Achtergrond instellen"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-or/strings.xml b/SecondaryDisplayLauncher/res/values-or/strings.xml
new file mode 100644
index 0000000..9bc5725
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-or/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"ଗତିବିଧିକୁ ଲଞ୍ଚ କରାଯାଇପାରିଲା ନାହିଁ"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"ଆପ୍‌ ସର୍ଟକଟ୍‌ ଯୋଗ କରନ୍ତୁ"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"ୱାଲ୍‌‌ପେପର୍‌କୁ ସେଟ୍ କରନ୍ତୁ"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-pa/strings.xml b/SecondaryDisplayLauncher/res/values-pa/strings.xml
new file mode 100644
index 0000000..c5dd582
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-pa/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"ਸਰਗਰਮੀ ਨੂੰ ਲਾਂਚ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"ਐਪ ਸ਼ਾਰਟਕੱਟ ਸ਼ਾਮਲ ਕਰੋ"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"ਵਾਲਪੇਪਰ ਸੈੱਟ ਕਰੋ"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-pl/strings.xml b/SecondaryDisplayLauncher/res/values-pl/strings.xml
new file mode 100644
index 0000000..e8efaed
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-pl/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Nie udało się uruchomić aktywności"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Dodaj skrót do aplikacji"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Ustaw tapetę"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-pt-rPT/strings.xml b/SecondaryDisplayLauncher/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..67c7557
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-pt-rPT/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Não foi possível iniciar a atividade."</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Adicionar atalho de aplicação"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Definir imagem de fundo"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-pt/strings.xml b/SecondaryDisplayLauncher/res/values-pt/strings.xml
new file mode 100644
index 0000000..201fc07
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-pt/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Não foi possível abrir a atividade"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Adicionar atalho de apps"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Definir plano de fundo"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-ro/strings.xml b/SecondaryDisplayLauncher/res/values-ro/strings.xml
new file mode 100644
index 0000000..e2e21c5
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-ro/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Nu s-a putut lansa activitatea"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Adăugați comanda rapidă pentru aplicație"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Setați imaginea de fundal"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-ru/strings.xml b/SecondaryDisplayLauncher/res/values-ru/strings.xml
new file mode 100644
index 0000000..64ba00e
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-ru/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Не удалось запустить объект activity"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Добавить ярлык приложения"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Установить обои"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-si/strings.xml b/SecondaryDisplayLauncher/res/values-si/strings.xml
new file mode 100644
index 0000000..ac492eb
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-si/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"ක්‍රියාකාරකම දියත් කිරීමට නොහැකි විය"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"යෙදුම් කෙටිමඟ එක් කරන්න"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"බිතුපත සකසන්න"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-sk/strings.xml b/SecondaryDisplayLauncher/res/values-sk/strings.xml
new file mode 100644
index 0000000..5e6fa7a
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-sk/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Aktivitu sa nepodarilo spustiť"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Pridať odkaz do aplikácie"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Nastaviť tapetu"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-sl/strings.xml b/SecondaryDisplayLauncher/res/values-sl/strings.xml
new file mode 100644
index 0000000..f54dec9
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-sl/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Dejavnosti ni bilo mogoče zagnati"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Dodaj bližnjico do aplikacije"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Nastavi ozadje"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-sq/strings.xml b/SecondaryDisplayLauncher/res/values-sq/strings.xml
new file mode 100644
index 0000000..e626dd1
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-sq/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Aktiviteti nuk mund të hapej"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Shto shkurtoren e aplikacionit"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Cakto imazhin e sfondit"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-sr/strings.xml b/SecondaryDisplayLauncher/res/values-sr/strings.xml
new file mode 100644
index 0000000..94214f1
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-sr/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Покретање активности није успело"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Додај пречицу за апликацију"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Подесите позадину"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-sv/strings.xml b/SecondaryDisplayLauncher/res/values-sv/strings.xml
new file mode 100644
index 0000000..53e17ef
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-sv/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Det gick inte att starta aktiviteten"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Lägg till appgenväg"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Ange bakgrund"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-sw/strings.xml b/SecondaryDisplayLauncher/res/values-sw/strings.xml
new file mode 100644
index 0000000..490561a
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-sw/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Imeshindwa kuanzisha shughuli"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Ongeza njia ya mkato ya programu"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Weka mandhari"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-sw600dp/dimens.xml b/SecondaryDisplayLauncher/res/values-sw600dp/dimens.xml
new file mode 100644
index 0000000..f33a8db
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-sw600dp/dimens.xml
@@ -0,0 +1,22 @@
+<?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>
+    <dimen name="app_picker_width">400dp</dimen>
+    <dimen name="app_picker_height">400dp</dimen>
+    <dimen name="app_picker_fab_margin">60dp</dimen>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-sw720dp/dimens.xml b/SecondaryDisplayLauncher/res/values-sw720dp/dimens.xml
new file mode 100644
index 0000000..524e52a
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-sw720dp/dimens.xml
@@ -0,0 +1,22 @@
+<?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>
+    <dimen name="app_picker_width">660dp</dimen>
+    <dimen name="app_picker_height">660dp</dimen>
+    <dimen name="app_picker_fab_margin">70dp</dimen>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-ta/strings.xml b/SecondaryDisplayLauncher/res/values-ta/strings.xml
new file mode 100644
index 0000000..6bb054a
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-ta/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"நடவடிக்கையைத் துவக்க இயலவில்லை"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"ஆப்ஸ் ஷார்ட்கட்டைச் சேர்"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"வால்பேப்பரை அமை"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-te/strings.xml b/SecondaryDisplayLauncher/res/values-te/strings.xml
new file mode 100644
index 0000000..3dd3c9b
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-te/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"కార్యకలాపాన్ని ప్రారంభించడం సాధ్యం కాలేదు"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"యాప్ షార్ట్‌కట్‌ని జోడించు"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"వాల్‌పేపర్‌ను సెట్ చేయండి"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-th/strings.xml b/SecondaryDisplayLauncher/res/values-th/strings.xml
new file mode 100644
index 0000000..6368950
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-th/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"เปิดกิจกรรมไม่ได้"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"เพิ่มทางลัดของแอป"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"ตั้งวอลเปเปอร์"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-tl/strings.xml b/SecondaryDisplayLauncher/res/values-tl/strings.xml
new file mode 100644
index 0000000..192e5c4
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-tl/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Hindi mailunsad ang aktibidad"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Magdagdag ng shortcut ng app"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Magtakda ng wallpaper"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-tr/strings.xml b/SecondaryDisplayLauncher/res/values-tr/strings.xml
new file mode 100644
index 0000000..e7ed998
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-tr/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"İşlem başlatılamadı"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Uygulama kısayolu ekle"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Duvar kağıdı ayarla"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-uk/strings.xml b/SecondaryDisplayLauncher/res/values-uk/strings.xml
new file mode 100644
index 0000000..e465995
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-uk/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Не вдалося запустити активність"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Розмістити ярлик додатка"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Вибрати фоновий малюнок"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-ur/strings.xml b/SecondaryDisplayLauncher/res/values-ur/strings.xml
new file mode 100644
index 0000000..e4c8641
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-ur/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"سرگرمی کو شروع نہیں کیا جا سکا"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"ایپ شارٹ کٹ شامل کریں"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"وال پیپر سیٹ کریں"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-uz/strings.xml b/SecondaryDisplayLauncher/res/values-uz/strings.xml
new file mode 100644
index 0000000..585739d
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-uz/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Faollik ishga tushmadi"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Yorliq yaratish"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Fonga rasm oʻrnatish"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-vi/strings.xml b/SecondaryDisplayLauncher/res/values-vi/strings.xml
new file mode 100644
index 0000000..15a1a44
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-vi/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Không thể chạy hoạt động"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Thêm lối tắt ứng dụng"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Đặt hình nền"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-zh-rCN/strings.xml b/SecondaryDisplayLauncher/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..3358499
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-zh-rCN/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"无法启动该操作组件"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"添加应用快捷方式"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"设置壁纸"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-zh-rHK/strings.xml b/SecondaryDisplayLauncher/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..bf76f29
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-zh-rHK/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"無法啟動活動"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"新增應用程式捷徑"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"設定桌布"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-zh-rTW/strings.xml b/SecondaryDisplayLauncher/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..bf76f29
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-zh-rTW/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"無法啟動活動"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"新增應用程式捷徑"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"設定桌布"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values-zu/strings.xml b/SecondaryDisplayLauncher/res/values-zu/strings.xml
new file mode 100644
index 0000000..ad2f6b9
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values-zu/strings.xml
@@ -0,0 +1,25 @@
+<?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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch" msgid="7873588052226763866">"Ayikwazanga ukuqalisa umsebenzi"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"Engeza isinqamuleli sohlelo lokusebenza"</string>
+    <string name="set_wallpaper" msgid="6475195450505435904">"Setha isithombe sangemuva"</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values/colors.xml b/SecondaryDisplayLauncher/res/values/colors.xml
new file mode 100644
index 0000000..66b41a2
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values/colors.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/colors.xml
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources>
+    <color name="launcher_bg_color">#884e8391</color>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values/dimens.xml b/SecondaryDisplayLauncher/res/values/dimens.xml
new file mode 100644
index 0000000..7cca607
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values/dimens.xml
@@ -0,0 +1,31 @@
+<?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>
+    <dimen name="app_list_col_width">72dp</dimen>
+    <dimen name="app_list_horizontal_spacing">24dp</dimen>
+    <dimen name="app_list_vertical_spacing">24dp</dimen>
+    <dimen name="app_icon_width">64dp</dimen>
+    <dimen name="app_icon_height">64dp</dimen>
+    <dimen name="app_grid_margin_top">24dp</dimen>
+    <dimen name="app_grid_margin_left">8dp</dimen>
+    <dimen name="app_grid_margin_right">8dp</dimen>
+
+    <dimen name="app_picker_width">300dp</dimen>
+    <dimen name="app_picker_height">300dp</dimen>
+    <dimen name="app_picker_fab_margin">20dp</dimen>
+    <dimen name="options_button_margin">20dp</dimen>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values/strings.xml b/SecondaryDisplayLauncher/res/values/strings.xml
new file mode 100644
index 0000000..b68918a
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values/strings.xml
@@ -0,0 +1,24 @@
+<?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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="couldnt_launch">Couldn\'t launch the activity</string>
+    <string name="add_app_shortcut">Add app shortcut</string>
+    <string name="set_wallpaper">Set wallpaper</string>
+</resources>
diff --git a/SecondaryDisplayLauncher/res/values/styles.xml b/SecondaryDisplayLauncher/res/values/styles.xml
new file mode 100644
index 0000000..4e41a98
--- /dev/null
+++ b/SecondaryDisplayLauncher/res/values/styles.xml
@@ -0,0 +1,30 @@
+<?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="SecondaryLauncherTheme" parent="Theme.MaterialComponents.NoActionBar" >
+        <item name="android:windowShowWallpaper">true</item>
+        <item name="android:windowNoTitle">true</item>
+        <item name="android:windowIsTranslucent">true</item>
+        <item name="android:windowTranslucentStatus">true</item>
+        <item name="android:windowTranslucentNavigation">true</item>
+        <item name="android:colorBackgroundCacheHint">@null</item>
+        <item name="android:windowBackground">@android:color/transparent</item>
+    </style>
+</resources>
diff --git a/SecondaryDisplayLauncher/src/com/android/launcher3/AppEntry.java b/SecondaryDisplayLauncher/src/com/android/launcher3/AppEntry.java
new file mode 100644
index 0000000..3017b81
--- /dev/null
+++ b/SecondaryDisplayLauncher/src/com/android/launcher3/AppEntry.java
@@ -0,0 +1,58 @@
+/**
+ * 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.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+
+/** An entry that represents a single activity that can be launched. */
+public  class AppEntry {
+
+    private String mLabel;
+    private Drawable mIcon;
+    private Intent mLaunchIntent;
+
+    AppEntry(ResolveInfo info, PackageManager packageManager) {
+        mLabel = info.loadLabel(packageManager).toString();
+        mIcon = info.loadIcon(packageManager);
+        mLaunchIntent = new Intent();
+        mLaunchIntent.setComponent(new ComponentName(info.activityInfo.packageName,
+                info.activityInfo.name));
+    }
+
+    String getLabel() {
+        return mLabel;
+    }
+
+    Drawable getIcon() {
+        return mIcon;
+    }
+
+    Intent getLaunchIntent() { return mLaunchIntent; }
+
+    ComponentName getComponentName() {
+        return mLaunchIntent.getComponent();
+    }
+
+    @Override
+    public String toString() {
+        return mLabel;
+    }
+}
diff --git a/SecondaryDisplayLauncher/src/com/android/launcher3/AppListAdapter.java b/SecondaryDisplayLauncher/src/com/android/launcher3/AppListAdapter.java
new file mode 100644
index 0000000..aa115cb
--- /dev/null
+++ b/SecondaryDisplayLauncher/src/com/android/launcher3/AppListAdapter.java
@@ -0,0 +1,63 @@
+/**
+ * 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.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.launcher3.R;
+
+import java.util.List;
+
+/** Adapter for available apps list. */
+public class AppListAdapter extends ArrayAdapter<AppEntry> {
+    private final LayoutInflater mInflater;
+
+    AppListAdapter(Context context) {
+        super(context, android.R.layout.simple_list_item_2);
+        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+    }
+
+    void setData(List<AppEntry> data) {
+        clear();
+        if (data != null) {
+            addAll(data);
+        }
+    }
+
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+        View view;
+
+        if (convertView == null) {
+            view = mInflater.inflate(R.layout.app_grid_item, parent, false);
+        } else {
+            view = convertView;
+        }
+
+        AppEntry item = getItem(position);
+        ((ImageView)view.findViewById(R.id.app_icon)).setImageDrawable(item.getIcon());
+        ((TextView)view.findViewById(R.id.app_name)).setText(item.getLabel());
+
+        return view;
+    }
+}
diff --git a/SecondaryDisplayLauncher/src/com/android/launcher3/AppListViewModel.java b/SecondaryDisplayLauncher/src/com/android/launcher3/AppListViewModel.java
new file mode 100644
index 0000000..914fd5e
--- /dev/null
+++ b/SecondaryDisplayLauncher/src/com/android/launcher3/AppListViewModel.java
@@ -0,0 +1,126 @@
+/**
+ * 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.Application;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.AsyncTask;
+
+import androidx.lifecycle.AndroidViewModel;
+import androidx.lifecycle.LiveData;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A view model that provides a list of activities that can be launched.
+ */
+public class AppListViewModel extends AndroidViewModel {
+
+    private final AppListLiveData mLiveData;
+    private final PackageIntentReceiver
+            mPackageIntentReceiver;
+
+    public AppListViewModel(Application application) {
+        super(application);
+        mLiveData = new AppListLiveData(application);
+        mPackageIntentReceiver = new PackageIntentReceiver(mLiveData, application);
+    }
+
+    public LiveData<List<AppEntry>> getAppList() {
+        return mLiveData;
+    }
+
+    protected void onCleared() {
+        getApplication().unregisterReceiver(mPackageIntentReceiver);
+    }
+}
+
+class AppListLiveData extends LiveData<List<AppEntry>> {
+
+    private final PackageManager mPackageManager;
+    private int mCurrentDataVersion;
+
+    public AppListLiveData(Context context) {
+        mPackageManager = context.getPackageManager();
+        loadData();
+    }
+
+    void loadData() {
+        final int loadDataVersion = ++mCurrentDataVersion;
+
+        new AsyncTask<Void, Void, List<AppEntry>>() {
+            @Override
+            protected List<AppEntry> doInBackground(Void... voids) {
+                Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
+                mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+
+                List<ResolveInfo> apps = mPackageManager.queryIntentActivities(mainIntent,
+                        PackageManager.GET_META_DATA);
+
+                List<AppEntry> entries = new ArrayList<>();
+                if (apps != null) {
+                    for (ResolveInfo app : apps) {
+                        AppEntry entry = new AppEntry(app, mPackageManager);
+                        entries.add(entry);
+                    }
+                }
+                return entries;
+            }
+
+            @Override
+            protected void onPostExecute(List<AppEntry> data) {
+                if (mCurrentDataVersion == loadDataVersion) {
+                    setValue(data);
+                }
+            }
+        }.execute();
+    }
+}
+
+/**
+ * Receiver used to notify live data about app list changes.
+ */
+class PackageIntentReceiver extends BroadcastReceiver {
+
+    private final AppListLiveData mLiveData;
+
+    public PackageIntentReceiver(AppListLiveData liveData, Context context) {
+        mLiveData = liveData;
+        IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        filter.addDataScheme("package");
+        context.registerReceiver(this, filter);
+
+        // Register for events related to sdcard installation.
+        IntentFilter sdFilter = new IntentFilter();
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+        context.registerReceiver(this, sdFilter);
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        mLiveData.loadData();
+    }
+}
\ No newline at end of file
diff --git a/SecondaryDisplayLauncher/src/com/android/launcher3/PinnedAppListViewModel.java b/SecondaryDisplayLauncher/src/com/android/launcher3/PinnedAppListViewModel.java
new file mode 100644
index 0000000..4f92038
--- /dev/null
+++ b/SecondaryDisplayLauncher/src/com/android/launcher3/PinnedAppListViewModel.java
@@ -0,0 +1,120 @@
+/**
+ * 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.PinnedAppListViewModel.PINNED_APPS_KEY;
+
+import android.app.Application;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.AsyncTask;
+
+import androidx.lifecycle.AndroidViewModel;
+import androidx.lifecycle.LiveData;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A view model that provides a list of activities that were pinned by user to always display on
+ * home screen.
+ * The pinned activities are stored in {@link SharedPreferences} to keep the sample simple :).
+ */
+public class PinnedAppListViewModel extends AndroidViewModel {
+
+    final static String PINNED_APPS_KEY = "pinned_apps";
+
+    private final PinnedAppListLiveData mLiveData;
+
+    public PinnedAppListViewModel(Application application) {
+        super(application);
+        mLiveData = new PinnedAppListLiveData(application);
+    }
+
+    public LiveData<List<AppEntry>> getPinnedAppList() {
+        return mLiveData;
+    }
+}
+
+class PinnedAppListLiveData extends LiveData<List<AppEntry>> {
+
+    private final Context mContext;
+    private final PackageManager mPackageManager;
+    // Store listener reference, so it won't be GC-ed.
+    private final SharedPreferences.OnSharedPreferenceChangeListener mChangeListener;
+    private int mCurrentDataVersion;
+
+    public PinnedAppListLiveData(Context context) {
+        mContext = context;
+        mPackageManager = context.getPackageManager();
+
+        final SharedPreferences prefs = context.getSharedPreferences(PINNED_APPS_KEY, 0);
+        mChangeListener = (preferences, key) -> {
+            loadData();
+        };
+        prefs.registerOnSharedPreferenceChangeListener(mChangeListener);
+
+        loadData();
+    }
+
+    private void loadData() {
+        final int loadDataVersion = ++mCurrentDataVersion;
+
+        new AsyncTask<Void, Void, List<AppEntry>>() {
+            @Override
+            protected List<AppEntry> doInBackground(Void... voids) {
+                List<AppEntry> entries = new ArrayList<>();
+
+                final SharedPreferences sp = mContext.getSharedPreferences(PINNED_APPS_KEY, 0);
+                final Set<String> pinnedAppsComponents = sp.getStringSet(PINNED_APPS_KEY, null);
+                if (pinnedAppsComponents == null) {
+                    return null;
+                }
+
+                for (String componentString : pinnedAppsComponents) {
+                    final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
+                    mainIntent.setComponent(ComponentName.unflattenFromString(componentString));
+                    mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+
+                    final List<ResolveInfo> apps = mPackageManager.queryIntentActivities(mainIntent,
+                            PackageManager.GET_META_DATA);
+
+                    if (apps != null) {
+                        for (ResolveInfo app : apps) {
+                            final AppEntry entry = new AppEntry(app, mPackageManager);
+                            entries.add(entry);
+                        }
+                    }
+                }
+
+                return entries;
+            }
+
+            @Override
+            protected void onPostExecute(List<AppEntry> data) {
+                if (mCurrentDataVersion == loadDataVersion) {
+                    setValue(data);
+                }
+            }
+        }.execute();
+    }
+}
\ No newline at end of file
diff --git a/SecondaryDisplayLauncher/src/com/android/launcher3/PinnedAppPickerDialog.java b/SecondaryDisplayLauncher/src/com/android/launcher3/PinnedAppPickerDialog.java
new file mode 100644
index 0000000..02e6e4a
--- /dev/null
+++ b/SecondaryDisplayLauncher/src/com/android/launcher3/PinnedAppPickerDialog.java
@@ -0,0 +1,74 @@
+/**
+ * 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.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.GridView;
+
+import androidx.fragment.app.DialogFragment;
+
+import com.android.launcher3.R;
+
+/**
+ * Callback to be invoked when an app was picked.
+ */
+interface AppPickedCallback {
+    void onAppPicked(AppEntry appEntry);
+}
+
+/**
+ * Dialog that provides the user with a list of available apps to pin to the home screen.
+ */
+public class PinnedAppPickerDialog extends DialogFragment {
+
+    private AppListAdapter mAppListAdapter;
+    private AppPickedCallback mAppPickerCallback;
+
+    public PinnedAppPickerDialog() {
+    }
+
+    public static PinnedAppPickerDialog newInstance(AppListAdapter appListAdapter,
+            AppPickedCallback callback) {
+        PinnedAppPickerDialog
+                frag = new PinnedAppPickerDialog();
+        frag.mAppListAdapter = appListAdapter;
+        frag.mAppPickerCallback = callback;
+        return frag;
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        return inflater.inflate(R.layout.app_picker_dialog, container);
+    }
+
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+
+        GridView appGridView = view.findViewById(R.id.picker_app_grid);
+        appGridView.setAdapter(mAppListAdapter);
+        appGridView.setOnItemClickListener((adapterView, itemView, position, id) -> {
+            final AppEntry entry = mAppListAdapter.getItem(position);
+            mAppPickerCallback.onAppPicked(entry);
+            dismiss();
+        });
+    }
+}
\ No newline at end of file
diff --git a/SecondaryDisplayLauncher/src/com/android/launcher3/SecondaryDisplayLauncher.java b/SecondaryDisplayLauncher/src/com/android/launcher3/SecondaryDisplayLauncher.java
new file mode 100644
index 0000000..0a2f18f
--- /dev/null
+++ b/SecondaryDisplayLauncher/src/com/android/launcher3/SecondaryDisplayLauncher.java
@@ -0,0 +1,229 @@
+/**
+ * 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.PinnedAppListViewModel.PINNED_APPS_KEY;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.app.AlertDialog;
+import android.app.Application;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewAnimationUtils;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.GridView;
+import android.widget.ImageButton;
+import android.widget.PopupMenu;
+
+import androidx.fragment.app.FragmentActivity;
+import androidx.fragment.app.FragmentManager;
+import androidx.lifecycle.ViewModelProvider;
+import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory;
+
+import com.google.android.material.circularreveal.cardview.CircularRevealCardView;
+import com.google.android.material.floatingactionbutton.FloatingActionButton;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Secondary launcher activity. It's launch mode is configured as "singleTop" to allow showing on
+ * multiple displays and to ensure a single instance per each display.
+ */
+public class SecondaryDisplayLauncher extends FragmentActivity implements AppPickedCallback,
+        PopupMenu.OnMenuItemClickListener {
+
+    private AppListAdapter mAppListAdapter;
+    private AppListAdapter mPinnedAppListAdapter;
+    private CircularRevealCardView mAppDrawerView;
+    private FloatingActionButton mFab;
+
+    private boolean mAppDrawerShown;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.secondary_display_launcher);
+
+        mAppDrawerView = findViewById(R.id.FloatingSheet);
+        mFab = findViewById(R.id.FloatingActionButton);
+
+        mFab.setOnClickListener((View v) -> {
+            showAppDrawer(true);
+        });
+
+        final ViewModelProvider viewModelProvider = new ViewModelProvider(getViewModelStore(),
+                new AndroidViewModelFactory((Application) getApplicationContext()));
+
+        mPinnedAppListAdapter = new AppListAdapter(this);
+        final GridView pinnedAppGridView = findViewById(R.id.pinned_app_grid);
+        pinnedAppGridView.setAdapter(mPinnedAppListAdapter);
+        pinnedAppGridView.setOnItemClickListener((adapterView, view, position, id) -> {
+            final AppEntry entry = mPinnedAppListAdapter.getItem(position);
+            launch(entry.getLaunchIntent());
+        });
+        final PinnedAppListViewModel pinnedAppListViewModel =
+                viewModelProvider.get(PinnedAppListViewModel.class);
+        pinnedAppListViewModel.getPinnedAppList().observe(this, data -> {
+            mPinnedAppListAdapter.setData(data);
+        });
+
+        mAppListAdapter = new AppListAdapter(this);
+        final GridView appGridView = findViewById(R.id.app_grid);
+        appGridView.setAdapter(mAppListAdapter);
+        appGridView.setOnItemClickListener((adapterView, view, position, id) -> {
+            final AppEntry entry = mAppListAdapter.getItem(position);
+            launch(entry.getLaunchIntent());
+        });
+        final AppListViewModel appListViewModel = viewModelProvider.get(AppListViewModel.class);
+        appListViewModel.getAppList().observe(this, data -> {
+            mAppListAdapter.setData(data);
+        });
+
+        ImageButton optionsButton = findViewById(R.id.OptionsButton);
+        optionsButton.setOnClickListener((View v) -> {
+            PopupMenu popup = new PopupMenu(this,v);
+            popup.setOnMenuItemClickListener(this);
+            MenuInflater inflater = popup.getMenuInflater();
+            inflater.inflate(R.menu.context_menu, popup.getMenu());
+            popup.show();
+        });
+    }
+
+    @Override
+    public boolean onMenuItemClick(MenuItem item) {
+        // Respond to picking one of the popup menu items.
+        final int id = item.getItemId();
+        if (id == R.id.add_app_shortcut) {
+            FragmentManager fm = getSupportFragmentManager();
+            PinnedAppPickerDialog pickerDialogFragment =
+                    PinnedAppPickerDialog.newInstance(mAppListAdapter, this);
+            pickerDialogFragment.show(fm, "fragment_app_picker");
+            return true;
+        } else if (id == R.id.set_wallpaper) {
+            Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER);
+            startActivity(Intent.createChooser(intent, getString(R.string.set_wallpaper)));
+            return true;
+        }
+
+        return true;
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        showAppDrawer(false);
+    }
+
+    public void onBackPressed() {
+        // If the app drawer was shown - hide it. Otherwise, not doing anything since we don't want
+        // to close the launcher.
+        showAppDrawer(false);
+    }
+
+    public void onNewIntent(Intent intent) {
+        super.onNewIntent(intent);
+
+        if (Intent.ACTION_MAIN.equals(intent.getAction())) {
+            // Hide keyboard.
+            final View v = getWindow().peekDecorView();
+            if (v != null && v.getWindowToken() != null) {
+                getSystemService(InputMethodManager.class).hideSoftInputFromWindow(
+                        v.getWindowToken(), 0);
+            }
+        }
+
+        // A new intent will bring the launcher to top. Hide the app drawer to reset the state.
+        showAppDrawer(false);
+    }
+
+    void launch(Intent launchIntent) {
+        launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        try {
+            startActivity(launchIntent);
+        } catch (Exception e) {
+            final AlertDialog.Builder builder =
+                    new AlertDialog.Builder(this, android.R.style.Theme_Material_Dialog_Alert);
+            builder.setTitle(R.string.couldnt_launch)
+                    .setMessage(e.getLocalizedMessage())
+                    .setIcon(android.R.drawable.ic_dialog_alert)
+                    .show();
+        }
+    }
+
+    /**
+     * Store the picked app to persistent pinned list and update the loader.
+     */
+    @Override
+    public void onAppPicked(AppEntry appEntry) {
+        final SharedPreferences sp = getSharedPreferences(PINNED_APPS_KEY, 0);
+        Set<String> pinnedApps = sp.getStringSet(PINNED_APPS_KEY, null);
+        if (pinnedApps == null) {
+            pinnedApps = new HashSet<String>();
+        } else {
+            // Always need to create a new object to make sure that the changes are persisted.
+            pinnedApps = new HashSet<String>(pinnedApps);
+        }
+        pinnedApps.add(appEntry.getComponentName().flattenToString());
+
+        final SharedPreferences.Editor editor = sp.edit();
+        editor.putStringSet(PINNED_APPS_KEY, pinnedApps);
+        editor.apply();
+    }
+
+    /**
+     * Show/hide app drawer card with animation.
+     */
+    private void showAppDrawer(boolean show) {
+        if (show == mAppDrawerShown) {
+            return;
+        }
+
+        final Animator animator = revealAnimator(mAppDrawerView, show);
+        if (show) {
+            mAppDrawerShown = true;
+            mAppDrawerView.setVisibility(View.VISIBLE);
+            mFab.setVisibility(View.INVISIBLE);
+        } else {
+            mAppDrawerShown = false;
+            animator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    super.onAnimationEnd(animation);
+                    mAppDrawerView.setVisibility(View.INVISIBLE);
+                    mFab.setVisibility(View.VISIBLE);
+                }
+            });
+        }
+        animator.start();
+    }
+
+    /**
+     * Create reveal/hide animator for app list card.
+     */
+    private Animator revealAnimator(View view, boolean open) {
+        final int radius = (int) Math.hypot((double) view.getWidth(), (double) view.getHeight());
+        return ViewAnimationUtils.createCircularReveal(view, view.getRight(), view.getBottom(),
+                open ? 0 : radius, open ? radius : 0);
+    }
+}
diff --git a/build.gradle b/build.gradle
index 4ae6600..e296455 100644
--- a/build.gradle
+++ b/build.gradle
@@ -4,27 +4,28 @@
         google()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:3.2.0-alpha12'
-        classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.3'
+        classpath GRADLE_CLASS_PATH
+        classpath PROTOBUF_CLASS_PATH
     }
 }
 
-final String SUPPORT_LIBS_VERSION = '28.0.0-SNAPSHOT'
+final String ANDROID_TOP = "${rootDir}/../../.."
+final String FRAMEWORK_PREBUILTS_DIR = "${ANDROID_TOP}/prebuilts/framework_intermediates/"
 
 apply plugin: 'com.android.application'
 apply plugin: 'com.google.protobuf'
 
 android {
-    compileSdkVersion 28
-    buildToolsVersion '28.0.0'
+    compileSdkVersion COMPILE_SDK
+    buildToolsVersion BUILD_TOOLS_VERSION
 
     defaultConfig {
-        minSdkVersion 21
+        minSdkVersion 25
         targetSdkVersion 28
         versionCode 1
         versionName "1.0"
 
-        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
         vectorDrawables.useSupportLibrary = true
     }
     buildTypes {
@@ -38,39 +39,56 @@
         targetCompatibility JavaVersion.VERSION_1_8
     }
 
-    flavorDimensions "default"
+    // The flavor dimensions for build variants (e.g. aospWithQuickstep, aospWithoutQuickstep)
+    // See: https://developer.android.com/studio/build/build-variants#flavor-dimensions
+    flavorDimensions "app", "recents"
 
     productFlavors {
         aosp {
-            dimension "default"
+            dimension "app"
             applicationId 'com.android.launcher3'
             testApplicationId 'com.android.launcher3.tests'
         }
 
         l3go {
-            dimension "default"
+            dimension "app"
             applicationId 'com.android.launcher3'
             testApplicationId 'com.android.launcher3.tests'
         }
 
-        quickstep {
-            dimension "default"
-            applicationId 'com.android.launcher3'
-            testApplicationId 'com.android.launcher3.tests'
+        withQuickstep {
+            dimension "recents"
+
+            minSdkVersion 28
+        }
+
+        withQuickstepIconRecents {
+            dimension "recents"
+
+            minSdkVersion 28
+        }
+
+        withoutQuickstep {
+            dimension "recents"
         }
     }
 
     // Disable release builds for now
     android.variantFilter { variant ->
         if (variant.buildType.name.endsWith('release')) {
-            variant.setIgnore(true);
+            variant.setIgnore(true)
+        }
+
+        // Icon recents is Go only
+        if (name.contains("WithQuickstepIconRecents") && !name.contains("l3go")) {
+            variant.setIgnore(true)
         }
     }
 
     sourceSets {
         main {
             res.srcDirs = ['res']
-            java.srcDirs = ['src']
+            java.srcDirs = ['src', 'src_plugins']
             manifest.srcFile 'AndroidManifest-common.xml'
             proto {
                 srcDir 'protos/'
@@ -84,7 +102,7 @@
 
         androidTest {
             res.srcDirs = ['tests/res']
-            java.srcDirs = ['tests/src']
+            java.srcDirs = ['tests/src', 'tests/tapl']
             manifest.srcFile "tests/AndroidManifest-common.xml"
         }
 
@@ -93,18 +111,29 @@
         }
 
         aosp {
-            java.srcDirs = ['src_flags', "src_ui_overrides"]
+            java.srcDirs = ['src_flags', 'src_shortcuts_overrides']
+            manifest.srcFile "AndroidManifest.xml"
         }
 
         l3go {
             res.srcDirs = ['go/res']
-            java.srcDirs = ['go/src_flags', "src_ui_overrides"]
+            java.srcDirs = ['go/src']
             manifest.srcFile "go/AndroidManifest.xml"
         }
 
-        quickstep {
-            res.srcDirs = ['quickstep/res']
-            java.srcDirs = ['src_flags', 'quickstep/src']
+        withoutQuickstep {
+            java.srcDirs = ['src_ui_overrides']
+        }
+
+        withQuickstep {
+            res.srcDirs = ['quickstep/res', 'quickstep/recents_ui_overrides/res']
+            java.srcDirs = ['quickstep/src', 'quickstep/recents_ui_overrides/src']
+            manifest.srcFile "quickstep/AndroidManifest.xml"
+        }
+
+        withQuickstepIconRecents {
+            res.srcDirs = ['quickstep/res', 'go/quickstep/res']
+            java.srcDirs = ['quickstep/src', 'go/quickstep/src']
             manifest.srcFile "quickstep/AndroidManifest.xml"
         }
     }
@@ -118,12 +147,20 @@
 }
 
 dependencies {
-    implementation "com.android.support:support-v4:${SUPPORT_LIBS_VERSION}"
-    implementation "com.android.support:support-dynamic-animation:${SUPPORT_LIBS_VERSION}"
-    implementation "com.android.support:recyclerview-v7:${SUPPORT_LIBS_VERSION}"
-    implementation 'com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-7'
+    implementation "androidx.dynamicanimation:dynamicanimation:${ANDROID_X_VERSION}"
+    implementation "androidx.recyclerview:recyclerview:${ANDROID_X_VERSION}"
+    implementation "androidx.preference:preference:${ANDROID_X_VERSION}"
+    implementation project(':IconLoader')
+    implementation fileTree(dir: "${FRAMEWORK_PREBUILTS_DIR}/libs", include: 'launcher_protos.jar')
 
-    quickstepImplementation fileTree(dir: "quickstep/libs", include: 'sysui_shared.jar')
+    // Recents lib dependency
+    withQuickstepImplementation fileTree(dir: "${FRAMEWORK_PREBUILTS_DIR}/quickstep/libs", include: 'sysui_shared.jar')
+
+    // Recents lib dependency for Go
+    withQuickstepIconRecentsImplementation fileTree(dir: "${FRAMEWORK_PREBUILTS_DIR}/quickstep/libs", include: 'sysui_shared.jar')
+
+    // Required for AOSP to compile. This is already included in the sysui_shared.jar
+    withoutQuickstepImplementation fileTree(dir: "${FRAMEWORK_PREBUILTS_DIR}/libs", include: 'plugin_core.jar')
 
     testImplementation 'junit:junit:4.12'
     androidTestImplementation "org.mockito:mockito-core:1.9.5"
@@ -132,7 +169,7 @@
     androidTestImplementation 'com.android.support.test:runner:1.0.0'
     androidTestImplementation 'com.android.support.test:rules:1.0.0'
     androidTestImplementation 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'
-    androidTestImplementation "com.android.support:support-annotations:${SUPPORT_LIBS_VERSION}"
+    androidTestImplementation "androidx.annotation:annotation:${ANDROID_X_VERSION}"
 }
 
 protobuf {
diff --git a/go/AndroidManifest.xml b/go/AndroidManifest.xml
index 0a9ad7b..fae1eff 100644
--- a/go/AndroidManifest.xml
+++ b/go/AndroidManifest.xml
@@ -22,7 +22,7 @@
     xmlns:tools="http://schemas.android.com/tools"
     package="com.android.launcher3" >
 
-    <uses-sdk android:targetSdkVersion="23" android:minSdkVersion="21"/>
+    <uses-sdk android:targetSdkVersion="28" android:minSdkVersion="25"/>
 
     <application
         android:backupAgent="com.android.launcher3.LauncherBackupAgent"
@@ -36,16 +36,14 @@
         android:restoreAnyVersion="true"
         android:supportsRtl="true" >
 
-        <!-- Activity for handling PinItemRequest. Only supports shortcuts -->
+        <!-- Activity for handling PinItemRequest is disabled on Android Go. -->
         <activity android:name="com.android.launcher3.dragndrop.AddItemActivity"
             android:theme="@android:style/Theme.DeviceDefault.Light.Dialog.Alert"
             android:excludeFromRecents="true"
             android:autoRemoveFromRecents="true"
             android:label="@string/action_add_to_workspace"
+            android:enabled="false"
             tools:node="replace" >
-            <intent-filter>
-                <action android:name="android.content.pm.action.CONFIRM_PIN_SHORTCUT" />
-            </intent-filter>
         </activity>
 
     </application>
diff --git a/go/quickstep/res/drawable/clear_all_button.xml b/go/quickstep/res/drawable/clear_all_button.xml
new file mode 100644
index 0000000..acac32d
--- /dev/null
+++ b/go/quickstep/res/drawable/clear_all_button.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid android:color="@color/clear_all_button_bg"/>
+    <corners android:radius="4dp"/>
+</shape>
diff --git a/go/quickstep/res/drawable/default_thumbnail.xml b/go/quickstep/res/drawable/default_thumbnail.xml
new file mode 100644
index 0000000..ab22dcf
--- /dev/null
+++ b/go/quickstep/res/drawable/default_thumbnail.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid android:color="@android:color/darker_gray"/>
+    <corners android:radius="@dimen/task_thumbnail_corner_radius"/>
+</shape>
diff --git a/go/quickstep/res/drawable/empty_content_box.xml b/go/quickstep/res/drawable/empty_content_box.xml
new file mode 100644
index 0000000..a488388
--- /dev/null
+++ b/go/quickstep/res/drawable/empty_content_box.xml
@@ -0,0 +1,23 @@
+<?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.
+-->
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid android:color="@android:color/transparent"/>
+    <stroke android:color="@android:color/white" android:width="4px"/>
+    <corners android:radius="2dp"/>
+</shape>
\ No newline at end of file
diff --git a/go/quickstep/res/layout/clear_all_button.xml b/go/quickstep/res/layout/clear_all_button.xml
new file mode 100644
index 0000000..eef66ad
--- /dev/null
+++ b/go/quickstep/res/layout/clear_all_button.xml
@@ -0,0 +1,34 @@
+<?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.
+-->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/clear_all_item_view"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/clear_all_item_view_height">
+    <Button
+        android:id="@+id/clear_all_button"
+        android:layout_width="@dimen/clear_all_button_width"
+        android:layout_height="match_parent"
+        android:layout_gravity="center_horizontal"
+        android:background="@drawable/clear_all_button"
+        android:gravity="center"
+        android:text="@string/recents_clear_all"
+        android:textAllCaps="false"
+        android:textColor="@color/clear_all_button_text"
+        android:textSize="14sp"
+        style="@style/TextTitle"/>
+</FrameLayout>
diff --git a/go/quickstep/res/layout/fallback_recents_activity.xml b/go/quickstep/res/layout/fallback_recents_activity.xml
new file mode 100644
index 0000000..653f463
--- /dev/null
+++ b/go/quickstep/res/layout/fallback_recents_activity.xml
@@ -0,0 +1,30 @@
+<?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.
+-->
+<com.android.quickstep.fallback.GoRecentsActivityRootView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/drag_layer"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:fitsSystemWindows="true">
+
+    <include
+        android:id="@+id/overview_panel"
+        layout="@layout/overview_panel"
+        android:clipChildren="false"
+        android:clipToPadding="false"
+        android:outlineProvider="none"
+        android:theme="@style/HomeScreenElementTheme" />
+</com.android.quickstep.fallback.GoRecentsActivityRootView>
diff --git a/go/quickstep/res/layout/icon_recents_root_view.xml b/go/quickstep/res/layout/icon_recents_root_view.xml
new file mode 100644
index 0000000..8381ebc
--- /dev/null
+++ b/go/quickstep/res/layout/icon_recents_root_view.xml
@@ -0,0 +1,41 @@
+<?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.
+-->
+<com.android.quickstep.views.IconRecentsView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:clipChildren="false">
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/recent_task_recycler_view"
+        android:layout_width="@dimen/recents_list_width"
+        android:layout_height="match_parent"
+        android:layout_gravity="center_horizontal"
+        android:scrollbars="none"
+        android:clipToPadding="false"
+        android:clipChildren="false"/>
+    <TextView
+        android:id="@+id/recent_task_empty_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center"
+        android:text="@string/recents_empty_message"
+        android:textColor="@android:color/white"
+        android:textSize="25sp"
+        style="@style/TextTitle"
+        android:visibility="gone"/>
+</com.android.quickstep.views.IconRecentsView>
\ No newline at end of file
diff --git a/go/quickstep/res/layout/overview_panel.xml b/go/quickstep/res/layout/overview_panel.xml
new file mode 100644
index 0000000..601edce
--- /dev/null
+++ b/go/quickstep/res/layout/overview_panel.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<fragment android:name="com.android.quickstep.IconRecentsFragment"
+          xmlns:android="http://schemas.android.com/apk/res/android"
+          android:id="@+id/low_ram_recents_fragment"
+          android:layout_width="match_parent"
+          android:layout_height="match_parent"/>
diff --git a/go/quickstep/res/layout/task_item_view.xml b/go/quickstep/res/layout/task_item_view.xml
new file mode 100644
index 0000000..aeac477
--- /dev/null
+++ b/go/quickstep/res/layout/task_item_view.xml
@@ -0,0 +1,46 @@
+<?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.
+-->
+<com.android.quickstep.views.TaskItemView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/task_item_height"
+    android:orientation="horizontal"
+    android:clipChildren="false">
+    <com.android.quickstep.views.TaskThumbnailIconView
+        android:id="@+id/task_icon_and_thumbnail"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_marginHorizontal="@dimen/task_thumbnail_icon_horiz_margin">
+        <ImageView
+            android:id="@+id/task_thumbnail"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"/>
+        <ImageView
+            android:id="@+id/task_icon"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"/>
+    </com.android.quickstep.views.TaskThumbnailIconView>
+    <TextView
+        android:id="@+id/task_label"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:singleLine="true"
+        android:textColor="@android:color/white"
+        android:textSize="24sp"
+        style="@style/TextTitle"/>
+</com.android.quickstep.views.TaskItemView>
diff --git a/go/quickstep/res/values-sw480dp/dimens.xml b/go/quickstep/res/values-sw480dp/dimens.xml
new file mode 100644
index 0000000..571b8a1
--- /dev/null
+++ b/go/quickstep/res/values-sw480dp/dimens.xml
@@ -0,0 +1,30 @@
+<?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>
+    <dimen name="recents_list_width">480dp</dimen>
+
+    <dimen name="task_item_height">90dp</dimen>
+    <dimen name="task_item_top_margin">24dp</dimen>
+    <dimen name="task_thumbnail_icon_horiz_margin">24dp</dimen>
+
+    <dimen name="task_thumbnail_corner_radius">4dp</dimen>
+
+    <dimen name="clear_all_item_view_height">52dp</dimen>
+    <dimen name="clear_all_item_view_top_margin">28dp</dimen>
+    <dimen name="clear_all_item_view_bottom_margin">28dp</dimen>
+    <dimen name="clear_all_button_width">160dp</dimen>
+</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..ff9dc9c
--- /dev/null
+++ b/go/quickstep/res/values/colors.xml
@@ -0,0 +1,20 @@
+<?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>
+    <color name="clear_all_button_bg">#FFDADCE0</color>
+    <color name="clear_all_button_text">#FF5F6368</color>
+</resources>
diff --git a/go/quickstep/res/values/dimens.xml b/go/quickstep/res/values/dimens.xml
new file mode 100644
index 0000000..91040f2
--- /dev/null
+++ b/go/quickstep/res/values/dimens.xml
@@ -0,0 +1,30 @@
+<?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>
+    <dimen name="recents_list_width">320dp</dimen>
+
+    <dimen name="task_item_height">60dp</dimen>
+    <dimen name="task_item_top_margin">16dp</dimen>
+    <dimen name="task_thumbnail_icon_horiz_margin">16dp</dimen>
+
+    <dimen name="task_thumbnail_corner_radius">3dp</dimen>
+
+    <dimen name="clear_all_item_view_height">36dp</dimen>
+    <dimen name="clear_all_item_view_top_margin">20dp</dimen>
+    <dimen name="clear_all_item_view_bottom_margin">20dp</dimen>
+    <dimen name="clear_all_button_width">106dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/go/quickstep/res/values/override.xml b/go/quickstep/res/values/override.xml
new file mode 100644
index 0000000..7636fb3
--- /dev/null
+++ b/go/quickstep/res/values/override.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- Class overrides for Go version of launcher with Go recents. -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+  <string name="app_transition_manager_class" translatable="false">com.android.launcher3.GoLauncherAppTransitionManagerImpl</string>
+
+  <string name="instant_app_resolver_class" translatable="false">com.android.quickstep.InstantAppResolverImpl</string>
+
+  <string name="main_process_initializer_class" translatable="false">com.android.quickstep.QuickstepProcessInitializer</string>
+</resources>
+
diff --git a/go/quickstep/src/com/android/launcher3/GoLauncherAppTransitionManagerImpl.java b/go/quickstep/src/com/android/launcher3/GoLauncherAppTransitionManagerImpl.java
new file mode 100644
index 0000000..bcb1f5c
--- /dev/null
+++ b/go/quickstep/src/com/android/launcher3/GoLauncherAppTransitionManagerImpl.java
@@ -0,0 +1,94 @@
+package com.android.launcher3;
+
+import static com.android.launcher3.Utilities.postAsyncCallback;
+import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
+import static com.android.quickstep.views.IconRecentsView.CONTENT_ALPHA;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
+
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.os.Handler;
+import android.view.View;
+
+import com.android.quickstep.views.IconRecentsView;
+import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+/**
+ * A {@link QuickstepAppTransitionManagerImpl} with recents-specific app transitions based off
+ * {@link com.android.quickstep.views.IconRecentsView}.
+ */
+public final class GoLauncherAppTransitionManagerImpl extends QuickstepAppTransitionManagerImpl {
+
+    public GoLauncherAppTransitionManagerImpl(Context context) {
+        super(context);
+    }
+
+    @Override
+    protected boolean isLaunchingFromRecents(View v, RemoteAnimationTargetCompat[] targets) {
+        return mLauncher.getStateManager().getState().overviewUi;
+    }
+
+    @Override
+    RemoteAnimationRunnerCompat getWallpaperOpenRunner(boolean fromUnlock) {
+        return new GoWallpaperOpenLauncherAnimationRunner(mHandler,
+                false /* startAtFrontOfQueue */, fromUnlock);
+    }
+
+    @Override
+    protected void composeRecentsLaunchAnimator(AnimatorSet anim, View v,
+            RemoteAnimationTargetCompat[] targets, boolean launcherClosing) {
+        // Stubbed. Recents launch animation will come from the recents view itself and will not
+        // use remote animations.
+    }
+
+    @Override
+    protected Runnable composeViewContentAnimator(AnimatorSet anim, float[] alphas, float[] trans) {
+        IconRecentsView overview = mLauncher.getOverviewPanel();
+        ObjectAnimator alpha = ObjectAnimator.ofFloat(overview,
+                CONTENT_ALPHA, alphas);
+        alpha.setDuration(CONTENT_ALPHA_DURATION);
+        alpha.setInterpolator(LINEAR);
+        anim.play(alpha);
+
+        ObjectAnimator transY = ObjectAnimator.ofFloat(overview, View.TRANSLATION_Y, trans);
+        transY.setInterpolator(AGGRESSIVE_EASE);
+        transY.setDuration(CONTENT_TRANSLATION_DURATION);
+        anim.play(transY);
+
+        return mLauncher.getStateManager()::reapplyState;
+    }
+
+    /**
+     * Remote animation runner for animation from app to Launcher. For Go, when going to recents,
+     * we need to ensure that the recents view is ready for remote animation before starting.
+     */
+    private final class GoWallpaperOpenLauncherAnimationRunner extends
+            WallpaperOpenLauncherAnimationRunner {
+        public GoWallpaperOpenLauncherAnimationRunner(Handler handler, boolean startAtFrontOfQueue,
+                boolean fromUnlock) {
+            super(handler, startAtFrontOfQueue, fromUnlock);
+        }
+
+        @Override
+        public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
+                AnimationResult result) {
+            boolean isGoingToRecents =
+                    taskIsATargetWithMode(targetCompats, mLauncher.getTaskId(), MODE_OPENING)
+                    && (mLauncher.getStateManager().getState() == LauncherState.OVERVIEW);
+            if (isGoingToRecents) {
+                IconRecentsView recentsView = mLauncher.getOverviewPanel();
+                if (!recentsView.isReadyForRemoteAnim()) {
+                    recentsView.setOnReadyForRemoteAnimCallback(() ->
+                        postAsyncCallback(mHandler, () -> onCreateAnimation(targetCompats, result))
+                    );
+                    return;
+                }
+            }
+            super.onCreateAnimation(targetCompats, result);
+        }
+    }
+}
diff --git a/go/quickstep/src/com/android/launcher3/LauncherRecentsToActivityHelper.java b/go/quickstep/src/com/android/launcher3/LauncherRecentsToActivityHelper.java
new file mode 100644
index 0000000..c12da94
--- /dev/null
+++ b/go/quickstep/src/com/android/launcher3/LauncherRecentsToActivityHelper.java
@@ -0,0 +1,38 @@
+/*
+ * 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;
+
+import static com.android.launcher3.LauncherState.NORMAL;
+
+import com.android.quickstep.RecentsToActivityHelper;
+
+/**
+ * {@link RecentsToActivityHelper} for when the recents implementation is contained in
+ * {@link Launcher}.
+ */
+public final class LauncherRecentsToActivityHelper implements RecentsToActivityHelper {
+
+    private final Launcher mLauncher;
+
+    public LauncherRecentsToActivityHelper(Launcher launcher) {
+        mLauncher = launcher;
+    }
+
+    @Override
+    public void leaveRecents() {
+        mLauncher.getStateManager().goToState(NORMAL);
+    }
+}
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
new file mode 100644
index 0000000..cbc77d2
--- /dev/null
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.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.launcher3.uioverrides;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherStateManager.StateHandler;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.graphics.RotationMode;
+import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController;
+import com.android.launcher3.uioverrides.touchcontrollers.LandscapeStatesTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController;
+import com.android.launcher3.util.TouchController;
+import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.views.IconRecentsView;
+
+import java.util.ArrayList;
+
+/**
+ * Provides recents-related {@link UiFactory} logic and classes.
+ */
+public abstract class RecentsUiFactory {
+
+    public static final boolean GO_LOW_RAM_RECENTS_ENABLED = true;
+
+    public static TouchController[] createTouchControllers(Launcher launcher) {
+        ArrayList<TouchController> list = new ArrayList<>();
+        list.add(launcher.getDragController());
+
+        if (launcher.getDeviceProfile().isVerticalBarLayout()) {
+            list.add(new LandscapeStatesTouchController(launcher));
+            list.add(new LandscapeEdgeSwipeController(launcher));
+        } else {
+            boolean allowDragToOverview = SysUINavigationMode.INSTANCE.get(launcher)
+                    .getMode().hasGestures;
+            list.add(new PortraitStatesTouchController(launcher, allowDragToOverview));
+        }
+        if (FeatureFlags.PULL_DOWN_STATUS_BAR && Utilities.IS_DEBUG_DEVICE
+                && !launcher.getDeviceProfile().isMultiWindowMode
+                && !launcher.getDeviceProfile().isVerticalBarLayout()) {
+            list.add(new StatusBarTouchController(launcher));
+        }
+        return list.toArray(new TouchController[list.size()]);
+    }
+
+    /**
+     * Creates and returns the controller responsible for recents view state transitions.
+     *
+     * @param launcher the launcher activity
+     * @return state handler for recents
+     */
+    public static StateHandler createRecentsViewStateController(Launcher launcher) {
+        return new RecentsViewStateController(launcher);
+    }
+
+    /**
+     * Clean-up logic that occurs when recents is no longer in use/visible.
+     *
+     * @param launcher the launcher activity
+     */
+    public static void resetOverview(Launcher launcher) {
+        IconRecentsView recentsView = launcher.getOverviewPanel();
+        recentsView.setTransitionedFromApp(false);
+    }
+
+    /**
+     * Recents logic that triggers when launcher state changes or launcher activity stops/resumes.
+     *
+     * @param launcher the launcher activity
+     */
+    public static void onLauncherStateOrResumeChanged(Launcher launcher) {}
+
+    public static RotationMode getRotationMode(DeviceProfile dp) {
+        return RotationMode.NORMAL;
+    }
+
+    public static void clearSwipeSharedState(boolean finishAnimation) {}
+}
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
new file mode 100644
index 0000000..0b12ab0
--- /dev/null
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -0,0 +1,44 @@
+/*
+ * 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 com.android.quickstep.views.IconRecentsView.CONTENT_ALPHA;
+
+import android.util.FloatProperty;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherRecentsToActivityHelper;
+import com.android.quickstep.views.IconRecentsView;
+
+import androidx.annotation.NonNull;
+
+/**
+ * State handler for Go's {@link IconRecentsView}.
+ */
+public final class RecentsViewStateController extends
+        BaseRecentsViewStateController<IconRecentsView> {
+
+    public RecentsViewStateController(@NonNull Launcher launcher) {
+        super(launcher);
+        launcher.<IconRecentsView>getOverviewPanel().setRecentsToActivityHelper(
+                new LauncherRecentsToActivityHelper(launcher));
+    }
+
+    @Override
+    FloatProperty<IconRecentsView> getContentAlphaProperty() {
+        return CONTENT_ALPHA;
+    }
+}
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/go/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
new file mode 100644
index 0000000..d0cfcf9
--- /dev/null
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -0,0 +1,158 @@
+/*
+ * 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 static android.view.View.VISIBLE;
+
+import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
+import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE;
+import static com.android.launcher3.anim.Interpolators.ACCEL;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
+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.states.RotationHelper.REQUEST_ROTATE;
+
+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.anim.AnimatorSetBuilder;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.views.IconRecentsView;
+
+/**
+ * Definition for overview state
+ */
+public class OverviewState extends LauncherState {
+
+    // Scale recents takes before animating in
+    private static final float RECENTS_PREPARE_SCALE = 1.33f;
+
+    private static final int STATE_FLAGS = FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED
+            | FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_DISABLE_ACCESSIBILITY;
+
+    public OverviewState(int id) {
+        this(id, OVERVIEW_TRANSITION_MS, STATE_FLAGS);
+    }
+
+    protected OverviewState(int id, int transitionDuration, int stateFlags) {
+        super(id, LauncherLogProto.ContainerType.TASKSWITCHER, transitionDuration, stateFlags);
+    }
+
+    @Override
+    public ScaleAndTranslation getOverviewScaleAndTranslation(Launcher launcher) {
+        return new ScaleAndTranslation(1f, 0f, 0f);
+    }
+
+    @Override
+    public void onStateEnabled(Launcher launcher) {
+        IconRecentsView recentsView = launcher.getOverviewPanel();
+        recentsView.onBeginTransitionToOverview();
+        recentsView.setShowStatusBarForegroundScrim(true);
+        // Request orientation be set to unspecified, letting the system decide the best
+        // orientation.
+        launcher.getRotationHelper().setCurrentStateRequest(REQUEST_ROTATE);
+    }
+
+    @Override
+    public void onStateDisabled(Launcher launcher) {
+        IconRecentsView recentsView = launcher.getOverviewPanel();
+        recentsView.setShowStatusBarForegroundScrim(false);
+    }
+
+    @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 NONE;
+    }
+
+    @Override
+    public float getWorkspaceScrimAlpha(Launcher launcher) {
+        return 0.5f;
+    }
+
+    @Override
+    public String getDescription(Launcher launcher) {
+        return launcher.getString(R.string.accessibility_desc_recent_apps);
+    }
+
+    @Override
+    public void onBackPressed(Launcher launcher) {
+        // TODO: Add logic to go back to task if coming from a currently running task.
+        super.onBackPressed(launcher);
+    }
+
+    public static float getDefaultSwipeHeight(Launcher launcher) {
+        return getDefaultSwipeHeight(launcher.getDeviceProfile());
+    }
+
+    public static float getDefaultSwipeHeight(DeviceProfile dp) {
+        return dp.allAppsCellHeightPx - dp.allAppsIconTextSizePx;
+    }
+
+    @Override
+    public void prepareForAtomicAnimation(Launcher launcher, LauncherState fromState,
+            AnimatorSetBuilder builder) {
+        if (fromState == NORMAL && this == OVERVIEW) {
+            if (SysUINavigationMode.getMode(launcher) == SysUINavigationMode.Mode.NO_BUTTON) {
+                builder.setInterpolator(ANIM_WORKSPACE_SCALE, ACCEL);
+                builder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL);
+            } else {
+                builder.setInterpolator(ANIM_WORKSPACE_SCALE, OVERSHOOT_1_2);
+            }
+            builder.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2);
+            builder.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2);
+            builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_7);
+            builder.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2);
+
+            View overview = launcher.getOverviewPanel();
+            if (overview.getVisibility() != VISIBLE) {
+                SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE);
+            }
+        }
+    }
+
+    public static OverviewState newBackgroundState(int id) {
+        return new OverviewState(id);
+    }
+
+    public static OverviewState newPeekState(int id) {
+        return new OverviewState(id);
+    }
+
+    public static OverviewState newSwitchState(int id) {
+        return new OverviewState(id);
+    }
+}
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeStatesTouchController.java b/go/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeStatesTouchController.java
new file mode 100644
index 0000000..1ccd7d7
--- /dev/null
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeStatesTouchController.java
@@ -0,0 +1,70 @@
+/*
+ * 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 android.view.MotionEvent;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+
+/**
+ * Touch controller for landscape mode.
+ */
+public final class LandscapeStatesTouchController extends PortraitStatesTouchController {
+
+    public LandscapeStatesTouchController(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 true;
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
+        if (fromState == ALL_APPS && !isDragTowardPositive) {
+            return NORMAL;
+        } else if (isDragTowardPositive) {
+            return ALL_APPS;
+        }
+        return fromState;
+    }
+
+    @Override
+    protected int getLogContainerTypeForNormalState() {
+        return LauncherLogProto.ContainerType.WORKSPACE;
+    }
+}
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java b/go/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java
new file mode 100644
index 0000000..011a4e7
--- /dev/null
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java
@@ -0,0 +1,65 @@
+/*
+ * 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 android.view.MotionEvent;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.util.PendingAnimation;
+
+/**
+ * Helper class for {@link PortraitStatesTouchController} that determines swipeable regions and
+ * animations on the overview state that depend on the recents implementation.
+ */
+public final class PortraitOverviewStateTouchHelper {
+
+    public PortraitOverviewStateTouchHelper(Launcher launcher) {}
+
+    /**
+     * Whether or not {@link PortraitStatesTouchController} should intercept the touch when on the
+     * overview state.
+     *
+     * @param ev the motion event
+     * @return true if we should intercept the motion event
+     */
+    boolean canInterceptTouch(MotionEvent ev) {
+        // Go does not support swiping to all-apps from recents.
+        return false;
+    }
+
+    /**
+     * Whether or not swiping down to leave overview state should return to the currently running
+     * task app.
+     *
+     * @return true if going back should take the user to the currently running task
+     */
+    boolean shouldSwipeDownReturnToApp() {
+        // Go does not support swiping tasks down to launch tasks from recents.
+        return false;
+    }
+
+    /**
+     * Create the animation for going from overview to the task app via swiping.
+     *
+     * @param duration how long the animation should be
+     * @return the animation
+     */
+    PendingAnimation createSwipeDownToTaskAppAnimation(long duration) {
+        // Go does not support swiping tasks down to launch tasks from recents.
+        return null;
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
new file mode 100644
index 0000000..fe159b5
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -0,0 +1,201 @@
+/*
+ * 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.Utilities.postAsyncCallback;
+import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.quickstep.views.IconRecentsView.REMOTE_APP_TO_OVERVIEW_DURATION;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
+
+import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
+import android.app.ActivityOptions;
+import android.os.Handler;
+import android.util.Log;
+
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.LauncherAnimationRunner;
+import com.android.quickstep.util.RemoteAnimationProvider;
+import com.android.quickstep.util.RemoteAnimationTargetSet;
+import com.android.quickstep.views.IconRecentsView;
+import com.android.systemui.shared.system.ActivityOptionsCompat;
+import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+/**
+ * Provider for the atomic remote window animation from the app to the overview.
+ *
+ * @param <T> activity that contains the overview
+ */
+final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> implements
+        RemoteAnimationProvider {
+    private static final String TAG = "AppToOverviewAnimationProvider";
+
+    private final ActivityControlHelper<T> mHelper;
+    private final int mTargetTaskId;
+    private IconRecentsView mRecentsView;
+    private AppToOverviewAnimationListener mAnimationReadyListener;
+
+    AppToOverviewAnimationProvider(ActivityControlHelper<T> helper, int targetTaskId) {
+        mHelper = helper;
+        mTargetTaskId = targetTaskId;
+    }
+
+    /**
+     * Set listener to various points in the animation preparing to animate.
+     *
+     * @param listener listener
+     */
+    void setAnimationListener(AppToOverviewAnimationListener listener) {
+        mAnimationReadyListener = listener;
+    }
+
+    /**
+     * 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) {
+        if (mAnimationReadyListener != null) {
+            mAnimationReadyListener.onActivityReady(activity);
+        }
+        ActivityControlHelper.AnimationFactory factory =
+                mHelper.prepareRecentsUI(activity, wasVisible,
+                        false /* animate activity */, (controller) -> {
+                            controller.dispatchOnStart();
+                            ValueAnimator anim = controller.getAnimationPlayer()
+                                    .setDuration(getRecentsLaunchDuration());
+                            anim.setInterpolator(FAST_OUT_SLOW_IN);
+                            anim.start();
+                        });
+        factory.onRemoteAnimationReceived(null);
+        factory.createActivityController(getRecentsLaunchDuration());
+        mRecentsView = activity.getOverviewPanel();
+        return false;
+    }
+
+    /**
+     * Create remote window animation from the currently running app to the overview panel. Should
+     * be called after {@link #onActivityReady}.
+     *
+     * @param targetCompats the target apps
+     * @return animation from app to overview
+     */
+    @Override
+    public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] targetCompats) {
+        if (mAnimationReadyListener != null) {
+            mAnimationReadyListener.onWindowAnimationCreated();
+        }
+        AnimatorSet anim = new AnimatorSet();
+        if (mRecentsView == null) {
+            if (Log.isLoggable(TAG, Log.WARN)) {
+                Log.w(TAG, "No recents view. Using stub animation.");
+            }
+            anim.play(ValueAnimator.ofInt(0, 1).setDuration(getRecentsLaunchDuration()));
+            return anim;
+        }
+
+        RemoteAnimationTargetSet targetSet =
+                new RemoteAnimationTargetSet(targetCompats, MODE_CLOSING);
+        mRecentsView.setTransitionedFromApp(!targetSet.isAnimatingHome());
+
+        RemoteAnimationTargetCompat recentsTarget = null;
+        RemoteAnimationTargetCompat closingAppTarget = null;
+
+        for (RemoteAnimationTargetCompat target : targetCompats) {
+            if (target.mode == MODE_OPENING) {
+                recentsTarget = target;
+            } else if (target.mode == MODE_CLOSING && target.taskId == mTargetTaskId) {
+                closingAppTarget = target;
+            }
+        }
+
+        if (closingAppTarget == null) {
+            if (Log.isLoggable(TAG, Log.WARN)) {
+                Log.w(TAG, "No closing app target. Using stub animation.");
+            }
+            anim.play(ValueAnimator.ofInt(0, 1).setDuration(getRecentsLaunchDuration()));
+            return anim;
+        }
+        if (recentsTarget == null) {
+            if (Log.isLoggable(TAG, Log.WARN)) {
+                Log.w(TAG, "No recents target. Using stub animation.");
+            }
+            anim.play(ValueAnimator.ofInt(0, 1).setDuration(getRecentsLaunchDuration()));
+            return anim;
+        }
+
+        if (closingAppTarget.activityType == ACTIVITY_TYPE_HOME) {
+            mRecentsView.playRemoteHomeToRecentsAnimation(anim, closingAppTarget, recentsTarget);
+        } else {
+            mRecentsView.playRemoteAppToRecentsAnimation(anim, closingAppTarget, recentsTarget);
+        }
+
+        return anim;
+    }
+
+    @Override
+    public ActivityOptions toActivityOptions(Handler handler, long duration) {
+        LauncherAnimationRunner runner = new LauncherAnimationRunner(handler,
+                false /* startAtFrontOfQueue */) {
+
+            @Override
+            public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
+                    AnimationResult result) {
+                IconRecentsView recentsView = mRecentsView;
+                if (!recentsView.isReadyForRemoteAnim()) {
+                    recentsView.setOnReadyForRemoteAnimCallback(() -> postAsyncCallback(handler,
+                            () -> onCreateAnimation(targetCompats, result))
+                    );
+                    return;
+                }
+                result.setAnimation(createWindowAnimation(targetCompats));
+            }
+        };
+        return ActivityOptionsCompat.makeRemoteAnimation(
+                new RemoteAnimationAdapterCompat(runner, duration,
+                        0 /* statusBarTransitionDelay */));
+    }
+
+    /**
+     * Get duration of animation from app to overview.
+     *
+     * @return duration of animation
+     */
+    long getRecentsLaunchDuration() {
+        return REMOTE_APP_TO_OVERVIEW_DURATION;
+    }
+
+    /**
+     * Listener for various points in the app to overview animation preparing to animate.
+     */
+    interface AppToOverviewAnimationListener {
+        /**
+         * Logic for when activity we're animating to is ready
+         *
+         * @param activity activity to animate to
+         */
+        void onActivityReady(BaseDraggingActivity activity);
+
+        /**
+         * Logic for when we've created the app to recents animation.
+         */
+        void onWindowAnimationCreated();
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/ClearAllHolder.java b/go/quickstep/src/com/android/quickstep/ClearAllHolder.java
new file mode 100644
index 0000000..ce87171
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/ClearAllHolder.java
@@ -0,0 +1,30 @@
+/*
+ * 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.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView.ViewHolder;
+
+/**
+ * Holder for clear all button view in task recycler view.
+ */
+final class ClearAllHolder extends ViewHolder {
+    public ClearAllHolder(@NonNull View itemView) {
+        super(itemView);
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/ContentFillItemAnimator.java b/go/quickstep/src/com/android/quickstep/ContentFillItemAnimator.java
new file mode 100644
index 0000000..808cd72
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/ContentFillItemAnimator.java
@@ -0,0 +1,281 @@
+/*
+ * 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.View.ALPHA;
+
+import static com.android.quickstep.TaskAdapter.CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT;
+import static com.android.quickstep.views.TaskItemView.CONTENT_TRANSITION_PROGRESS;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView.ViewHolder;
+import androidx.recyclerview.widget.SimpleItemAnimator;
+
+import com.android.quickstep.views.TaskItemView;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * An item animator that is only set and used for the transition from the empty loading UI to
+ * the filled task content UI. The animation starts from the bottom to top, changing all valid
+ * empty item views to be filled and removing all extra empty views.
+ */
+public final class ContentFillItemAnimator extends SimpleItemAnimator {
+
+    private static final class PendingAnimation {
+        ViewHolder viewHolder;
+        int animType;
+
+        PendingAnimation(ViewHolder vh, int type) {
+            viewHolder = vh;
+            animType = type;
+        }
+    }
+
+    private static final int ANIM_TYPE_REMOVE = 0;
+    private static final int ANIM_TYPE_CHANGE = 1;
+
+    private static final int ITEM_BETWEEN_DELAY = 40;
+    private static final int ITEM_CHANGE_DURATION = 150;
+    private static final int ITEM_REMOVE_DURATION = 150;
+
+    /**
+     * Animations that have been registered to occur together at the next call of
+     * {@link #runPendingAnimations()} but have not started.
+     */
+    private final ArrayList<PendingAnimation> mPendingAnims = new ArrayList<>();
+
+    /**
+     * Animations that have started and are running.
+     */
+    private final ArrayList<ObjectAnimator> mRunningAnims = new ArrayList<>();
+
+    private Runnable mOnFinishRunnable;
+
+    /**
+     * Set runnable to run after the content fill animation is fully completed.
+     *
+     * @param runnable runnable to run on end
+     */
+    public void setOnAnimationFinishedRunnable(Runnable runnable) {
+        mOnFinishRunnable = runnable;
+    }
+
+    @Override
+    public void setChangeDuration(long changeDuration) {
+        throw new UnsupportedOperationException("Cascading item animator cannot have animation "
+                + "duration changed.");
+    }
+
+    @Override
+    public void setRemoveDuration(long removeDuration) {
+        throw new UnsupportedOperationException("Cascading item animator cannot have animation "
+                + "duration changed.");
+    }
+
+    @Override
+    public boolean animateRemove(ViewHolder holder) {
+        PendingAnimation pendAnim = new PendingAnimation(holder, ANIM_TYPE_REMOVE);
+        mPendingAnims.add(pendAnim);
+        return true;
+    }
+
+    private void animateRemoveImpl(ViewHolder holder, long startDelay) {
+        final View view = holder.itemView;
+        if (holder.itemView.getAlpha() == 0) {
+            // View is already visually removed. We can just get rid of it now.
+            view.setAlpha(1.0f);
+            dispatchRemoveFinished(holder);
+            dispatchFinishedWhenDone();
+            return;
+        }
+        final ObjectAnimator anim = ObjectAnimator.ofFloat(
+                holder.itemView, ALPHA, holder.itemView.getAlpha(), 0.0f);
+        anim.setDuration(ITEM_REMOVE_DURATION).setStartDelay(startDelay);
+        anim.addListener(
+                new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationStart(Animator animation) {
+                        dispatchRemoveStarting(holder);
+                    }
+
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        view.setAlpha(1);
+                        dispatchRemoveFinished(holder);
+                        mRunningAnims.remove(anim);
+                        dispatchFinishedWhenDone();
+                    }
+                }
+        );
+        anim.start();
+        mRunningAnims.add(anim);
+    }
+
+    @Override
+    public boolean animateAdd(ViewHolder holder) {
+        dispatchAddFinished(holder);
+        return false;
+    }
+
+    @Override
+    public boolean animateMove(ViewHolder holder, int fromX, int fromY, int toX,
+            int toY) {
+        dispatchMoveFinished(holder);
+        return false;
+    }
+
+    @Override
+    public boolean animateChange(ViewHolder oldHolder,
+            ViewHolder newHolder, int fromLeft, int fromTop, int toLeft, int toTop) {
+        // Only support changes where the holders are the same
+        if (oldHolder == newHolder) {
+            PendingAnimation pendAnim = new PendingAnimation(oldHolder, ANIM_TYPE_CHANGE);
+            mPendingAnims.add(pendAnim);
+            return true;
+        }
+        dispatchChangeFinished(oldHolder, true /* oldItem */);
+        dispatchChangeFinished(newHolder, false /* oldItem */);
+        return false;
+    }
+
+    private void animateChangeImpl(ViewHolder viewHolder, long startDelay) {
+        TaskItemView itemView = (TaskItemView) viewHolder.itemView;
+        if (itemView.getAlpha() == 0) {
+            // View is still not visible, so we can finish the change immediately.
+            CONTENT_TRANSITION_PROGRESS.set(itemView, 1.0f);
+            dispatchChangeFinished(viewHolder, true /* oldItem */);
+            dispatchFinishedWhenDone();
+            return;
+        }
+        final ObjectAnimator anim =
+                ObjectAnimator.ofFloat(itemView, CONTENT_TRANSITION_PROGRESS, 0.0f, 1.0f);
+        anim.setDuration(ITEM_CHANGE_DURATION).setStartDelay(startDelay);
+        anim.addListener(
+                new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationStart(Animator animation) {
+                        dispatchChangeStarting(viewHolder, true /* oldItem */);
+                    }
+
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        CONTENT_TRANSITION_PROGRESS.set(itemView, 1.0f);
+                        dispatchChangeFinished(viewHolder, true /* oldItem */);
+                        mRunningAnims.remove(anim);
+                        dispatchFinishedWhenDone();
+                    }
+                }
+        );
+        anim.start();
+        mRunningAnims.add(anim);
+    }
+
+    @Override
+    public void runPendingAnimations() {
+        // Run animations bottom to top.
+        mPendingAnims.sort(Comparator.comparingInt(o -> -o.viewHolder.itemView.getBottom()));
+        int delay = 0;
+        while (!mPendingAnims.isEmpty()) {
+            PendingAnimation curAnim = mPendingAnims.remove(0);
+            ViewHolder vh = curAnim.viewHolder;
+            switch (curAnim.animType) {
+                case ANIM_TYPE_REMOVE:
+                    animateRemoveImpl(vh, delay);
+                    break;
+                case ANIM_TYPE_CHANGE:
+                    animateChangeImpl(vh, delay);
+                    break;
+                default:
+                    break;
+            }
+            delay += ITEM_BETWEEN_DELAY;
+        }
+    }
+
+    @Override
+    public void endAnimation(@NonNull ViewHolder item) {
+        for (int i = mPendingAnims.size() - 1; i >= 0; i--) {
+            endPendingAnimation(mPendingAnims.get(i));
+            mPendingAnims.remove(i);
+        }
+        dispatchFinishedWhenDone();
+    }
+
+    @Override
+    public void endAnimations() {
+        if (!isRunning()) {
+            return;
+        }
+        for (int i = mPendingAnims.size() - 1; i >= 0; i--) {
+            endPendingAnimation(mPendingAnims.get(i));
+            mPendingAnims.remove(i);
+        }
+        for (int i = mRunningAnims.size() - 1; i >= 0; i--) {
+            ObjectAnimator anim = mRunningAnims.get(i);
+            // This calls the on end animation callback which will set values to their end target.
+            anim.cancel();
+        }
+        dispatchFinishedWhenDone();
+    }
+
+    private void endPendingAnimation(PendingAnimation pendAnim) {
+        ViewHolder item = pendAnim.viewHolder;
+        switch (pendAnim.animType) {
+            case ANIM_TYPE_REMOVE:
+                item.itemView.setAlpha(1.0f);
+                dispatchRemoveFinished(item);
+                break;
+            case ANIM_TYPE_CHANGE:
+                CONTENT_TRANSITION_PROGRESS.set(item.itemView, 1.0f);
+                dispatchChangeFinished(item, true /* oldItem */);
+                break;
+            default:
+                break;
+        }
+    }
+
+    @Override
+    public boolean isRunning() {
+        return !mPendingAnims.isEmpty() || !mRunningAnims.isEmpty();
+    }
+
+    @Override
+    public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
+            @NonNull List<Object> payloads) {
+        if (!payloads.isEmpty()
+                && (int) payloads.get(0) == CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT) {
+            return true;
+        }
+        return super.canReuseUpdatedViewHolder(viewHolder, payloads);
+    }
+
+    private void dispatchFinishedWhenDone() {
+        if (!isRunning()) {
+            dispatchAnimationsFinished();
+            if (mOnFinishRunnable != null) {
+                mOnFinishRunnable.run();
+            }
+        }
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java b/go/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java
new file mode 100644
index 0000000..057b48b
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java
@@ -0,0 +1,119 @@
+/*
+ * 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.anim.Interpolators.LINEAR;
+import static com.android.quickstep.views.IconRecentsView.CONTENT_ALPHA;
+
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.graphics.Rect;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.quickstep.util.RemoteAnimationTargetSet;
+import com.android.quickstep.views.IconRecentsView;
+
+import java.util.function.BiPredicate;
+import java.util.function.Consumer;
+
+/**
+ * {@link ActivityControlHelper} for recents when the default launcher is different than the
+ * currently running one and apps should interact with the {@link RecentsActivity} as opposed
+ * to the in-launcher one.
+ */
+public final class FallbackActivityControllerHelper extends
+        GoActivityControlHelper<RecentsActivity> {
+
+    public FallbackActivityControllerHelper() { }
+
+    @Override
+    public AnimationFactory prepareRecentsUI(RecentsActivity activity, boolean activityVisible,
+            boolean animateActivity, Consumer<AnimatorPlaybackController> callback) {
+        if (activityVisible) {
+            return (transitionLength) -> { };
+        }
+
+        IconRecentsView rv = activity.getOverviewPanel();
+        rv.setUsingRemoteAnimation(true);
+        rv.setAlpha(0);
+
+        return new AnimationFactory() {
+
+            boolean isAnimatingToRecents = false;
+
+            @Override
+            public void onRemoteAnimationReceived(RemoteAnimationTargetSet targets) {
+                isAnimatingToRecents = targets != null && targets.isAnimatingHome();
+                if (!isAnimatingToRecents) {
+                    rv.setAlpha(1);
+                }
+                createActivityController(getSwipeUpDestinationAndLength(
+                        activity.getDeviceProfile(), activity, new Rect()));
+            }
+
+            @Override
+            public void createActivityController(long transitionLength) {
+                if (!isAnimatingToRecents) {
+                    return;
+                }
+
+                ObjectAnimator anim = ObjectAnimator.ofFloat(rv, CONTENT_ALPHA, 0, 1);
+                anim.setDuration(transitionLength).setInterpolator(LINEAR);
+                AnimatorSet animatorSet = new AnimatorSet();
+                animatorSet.play(anim);
+                callback.accept(AnimatorPlaybackController.wrap(animatorSet, transitionLength));
+            }
+        };
+    }
+
+    @Override
+    public ActivityInitListener createActivityInitListener(
+            BiPredicate<RecentsActivity, Boolean> onInitListener) {
+        return new RecentsActivityTracker(onInitListener);
+    }
+
+    @Nullable
+    @Override
+    public RecentsActivity getCreatedActivity() {
+        return RecentsActivityTracker.getCurrentActivity();
+    }
+
+    @Nullable
+    @Override
+    public IconRecentsView getVisibleRecentsView() {
+        RecentsActivity activity = getCreatedActivity();
+        if (activity != null && activity.hasWindowFocus()) {
+            return activity.getOverviewPanel();
+        }
+        return null;
+    }
+
+    @Override
+    public boolean switchToRecentsIfVisible(Runnable onCompleteCallback) {
+        return false;
+    }
+
+    @Override
+    public int getContainerType() {
+        return LauncherLogProto.ContainerType.SIDELOADED_LAUNCHER;
+    }
+
+    @Override
+    public void onLaunchTaskSuccess(RecentsActivity activity) { }
+}
diff --git a/go/quickstep/src/com/android/quickstep/FallbackRecentsToActivityHelper.java b/go/quickstep/src/com/android/quickstep/FallbackRecentsToActivityHelper.java
new file mode 100644
index 0000000..a845f93
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/FallbackRecentsToActivityHelper.java
@@ -0,0 +1,34 @@
+/*
+ * 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;
+
+/**
+ * {@link RecentsToActivityHelper} for when we are using the fallback recents in
+ * {@link BaseRecentsActivity}.
+ */
+public final class FallbackRecentsToActivityHelper implements RecentsToActivityHelper {
+
+    BaseRecentsActivity mActivity;
+
+    public FallbackRecentsToActivityHelper(BaseRecentsActivity activity) {
+        mActivity = activity;
+    }
+
+    @Override
+    public void leaveRecents() {
+        mActivity.startHome();
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/GoActivityControlHelper.java b/go/quickstep/src/com/android/quickstep/GoActivityControlHelper.java
new file mode 100644
index 0000000..274a347
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/GoActivityControlHelper.java
@@ -0,0 +1,69 @@
+package com.android.quickstep;
+
+import android.content.Context;
+import android.graphics.Rect;
+
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+/**
+ * Base activity control helper for Go that stubs out most of the functionality that is not needed
+ * for Go.
+ *
+ * @param <T> activity that contains the overview
+ */
+public abstract class GoActivityControlHelper<T extends BaseDraggingActivity> implements
+        ActivityControlHelper<T> {
+
+    @Override
+    public void onTransitionCancelled(T activity, boolean activityVisible) {
+        // Go transitions to overview are all atomic.
+    }
+
+    @Override
+    public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect) {
+        // TODO Implement outRect depending on where the task should animate to.
+        // Go does not support swipe up gesture.
+        return 0;
+    }
+
+    @Override
+    public void onSwipeUpToRecentsComplete(T activity) {
+        // Go does not support swipe up gesture.
+    }
+
+    @Override
+    public void onAssistantVisibilityChanged(float visibility) {
+        // Go does not support assistant visibility transitions.
+    }
+
+    @Override
+    public HomeAnimationFactory prepareHomeUI(T activity) {
+        // Go does not support gestures from app to home.
+        return null;
+    }
+
+    @Override
+    public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target) {
+        // Go does not support gestures to overview.
+        return null;
+    }
+
+    @Override
+    public boolean shouldMinimizeSplitScreen() {
+        // Go does not support split screen.
+        return true;
+    }
+
+    @Override
+    public boolean isInLiveTileMode() {
+        // Go does not support live tiles.
+        return false;
+    }
+
+    @Override
+    public void onLaunchTaskFailed(T activity) {
+        // Go does not support gestures from one task to another.
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/IconRecentsFragment.java b/go/quickstep/src/com/android/quickstep/IconRecentsFragment.java
new file mode 100644
index 0000000..facf0d2
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/IconRecentsFragment.java
@@ -0,0 +1,37 @@
+/*
+ * 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.app.Fragment;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.R;
+
+public class IconRecentsFragment extends Fragment {
+    @Nullable
+    @Override
+    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+            @Nullable Bundle savedInstanceState) {
+        return inflater.inflate(R.layout.icon_recents_root_view, container, false);
+    }
+}
\ No newline at end of file
diff --git a/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java b/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java
new file mode 100644
index 0000000..b0d9cda
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java
@@ -0,0 +1,112 @@
+/*
+ * 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.OVERVIEW;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherInitListener;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.quickstep.views.IconRecentsView;
+
+import java.util.function.BiPredicate;
+import java.util.function.Consumer;
+
+/**
+ * {@link ActivityControlHelper} for the in-launcher recents.
+ * TODO: Implement the app to overview animation functionality
+ */
+public final class LauncherActivityControllerHelper extends GoActivityControlHelper<Launcher> {
+
+    @Override
+    public AnimationFactory prepareRecentsUI(Launcher activity,
+            boolean activityVisible, boolean animateActivity,
+            Consumer<AnimatorPlaybackController> callback) {
+        LauncherState fromState = activity.getStateManager().getState();
+        activity.<IconRecentsView>getOverviewPanel().setUsingRemoteAnimation(true);
+        //TODO: Implement this based off where the recents view needs to be for app => recents anim.
+        return new AnimationFactory() {
+            @Override
+            public void createActivityController(long transitionLength) {
+                callback.accept(activity.getStateManager().createAnimationToNewWorkspace(
+                        fromState, OVERVIEW, transitionLength));
+            }
+
+            @Override
+            public void onTransitionCancelled() {}
+        };
+    }
+
+    @Override
+    public ActivityInitListener createActivityInitListener(
+            BiPredicate<Launcher, Boolean> onInitListener) {
+        return new LauncherInitListener(onInitListener);
+    }
+
+    @Override
+    public Launcher getCreatedActivity() {
+        LauncherAppState app = LauncherAppState.getInstanceNoCreate();
+        if (app == null) {
+            return null;
+        }
+        return (Launcher) app.getModel().getCallback();
+    }
+
+    private Launcher getVisibleLauncher() {
+        Launcher launcher = getCreatedActivity();
+        return (launcher != null) && launcher.isStarted() && launcher.hasWindowFocus() ?
+                launcher : null;
+    }
+
+    @Override
+    public IconRecentsView getVisibleRecentsView() {
+        Launcher launcher = getVisibleLauncher();
+        return launcher != null && launcher.getStateManager().getState().overviewUi
+                ? launcher.getOverviewPanel() : null;
+    }
+
+    @Override
+    public boolean switchToRecentsIfVisible(Runnable onCompleteCallback) {
+        Launcher launcher = getVisibleLauncher();
+        if (launcher == null) {
+            return false;
+        }
+        launcher.<IconRecentsView>getOverviewPanel().setUsingRemoteAnimation(false);
+        launcher.getUserEventDispatcher().logActionCommand(
+                LauncherLogProto.Action.Command.RECENTS_BUTTON,
+                getContainerType(),
+                LauncherLogProto.ContainerType.TASKSWITCHER);
+        launcher.getStateManager().goToState(OVERVIEW,
+                launcher.getStateManager().shouldAnimateStateChange(), onCompleteCallback);
+        return true;
+    }
+
+    @Override
+    public int getContainerType() {
+        final Launcher launcher = getVisibleLauncher();
+        return launcher != null ? launcher.getStateManager().getState().containerType
+                : LauncherLogProto.ContainerType.APP;
+    }
+
+    @Override
+    public void onLaunchTaskSuccess(Launcher launcher) {
+        launcher.getStateManager().moveToRestState();
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/go/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
new file mode 100644
index 0000000..0fa3d86
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -0,0 +1,181 @@
+/*
+ * 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.systemui.shared.system.ActivityManagerWrapper
+        .CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.os.Build;
+import android.os.SystemClock;
+import android.view.ViewConfiguration;
+
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
+import com.android.quickstep.AppToOverviewAnimationProvider.AppToOverviewAnimationListener;
+import com.android.quickstep.views.IconRecentsView;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.LatencyTrackerCompat;
+
+/**
+ * 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 ActivityManagerWrapper mAM;
+    private final RecentsModel mRecentsModel;
+    private final MainThreadExecutor mMainThreadExecutor;
+    private final OverviewComponentObserver mOverviewComponentObserver;
+
+    private long mLastToggleTime;
+
+    public OverviewCommandHelper(Context context, OverviewComponentObserver observer) {
+        mContext = context;
+        mAM = ActivityManagerWrapper.getInstance();
+        mMainThreadExecutor = new MainThreadExecutor();
+        mRecentsModel = RecentsModel.INSTANCE.get(mContext);
+        mOverviewComponentObserver = observer;
+    }
+
+    public void onOverviewToggle() {
+        // If currently screen pinning, do not enter overview
+        if (mAM.isScreenPinningActive()) {
+            return;
+        }
+
+        mAM.closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+        mMainThreadExecutor.execute(new RecentsActivityCommand<>());
+    }
+
+    public void onOverviewShown(boolean triggeredFromAltTab) {
+        mMainThreadExecutor.execute(new ShowRecentsCommand());
+    }
+
+    public void onOverviewHidden() {
+        mMainThreadExecutor.execute(new HideRecentsCommand());
+    }
+
+    public void onTip(int actionType, int viewType) {
+        mMainThreadExecutor.execute(() ->
+                UserEventDispatcher.newInstance(mContext).logActionTip(actionType, viewType));
+    }
+
+    private class ShowRecentsCommand extends RecentsActivityCommand {
+
+        @Override
+        protected boolean handleCommand(long elapsedTime) {
+            return mHelper.getVisibleRecentsView() != null;
+        }
+    }
+
+    private class HideRecentsCommand extends RecentsActivityCommand {
+
+        @Override
+        protected boolean handleCommand(long elapsedTime) {
+            IconRecentsView recents = (IconRecentsView) mHelper.getVisibleRecentsView();
+            if (recents == null) {
+                return false;
+            }
+            recents.handleOverviewCommand();
+            return true;
+        }
+    }
+
+    private class RecentsActivityCommand<T extends BaseDraggingActivity> implements Runnable {
+
+        protected final ActivityControlHelper<T> mHelper;
+        private final long mCreateTime;
+
+        private final long mToggleClickedTime = SystemClock.uptimeMillis();
+        private boolean mUserEventLogged;
+        private ActivityInitListener mListener;
+
+        public RecentsActivityCommand() {
+            mHelper = mOverviewComponentObserver.getActivityControlHelper();
+            mCreateTime = SystemClock.elapsedRealtime();
+
+            // Preload the plan
+            mRecentsModel.getTasks(null);
+        }
+
+        @Override
+        public void run() {
+            long elapsedTime = mCreateTime - mLastToggleTime;
+            mLastToggleTime = mCreateTime;
+
+            if (handleCommand(elapsedTime)) {
+                // Command already handled.
+                return;
+            }
+
+            if (mHelper.switchToRecentsIfVisible(null /* onCompleteCallback */)) {
+                // If successfully switched, then return
+                return;
+            }
+
+            AppToOverviewAnimationProvider<T> provider =
+                    new AppToOverviewAnimationProvider<>(mHelper, RecentsModel.getRunningTaskId());
+            provider.setAnimationListener(
+                    new AppToOverviewAnimationListener() {
+                        @Override
+                        public void onActivityReady(BaseDraggingActivity activity) {
+                            if (!mUserEventLogged) {
+                                activity.getUserEventDispatcher().logActionCommand(
+                                        LauncherLogProto.Action.Command.RECENTS_BUTTON,
+                                        mHelper.getContainerType(),
+                                        LauncherLogProto.ContainerType.TASKSWITCHER);
+                                mUserEventLogged = true;
+                            }
+                        }
+
+                        @Override
+                        public void onWindowAnimationCreated() {
+                            if (LatencyTrackerCompat.isEnabled(mContext)) {
+                                LatencyTrackerCompat.logToggleRecents(
+                                        (int) (SystemClock.uptimeMillis() - mToggleClickedTime));
+                            }
+
+                            mListener.unregister();
+                        }
+                    });
+
+            // Otherwise, start overview.
+            mListener = mHelper.createActivityInitListener(provider::onActivityReady);
+            mListener.registerAndStartActivity(mOverviewComponentObserver.getOverviewIntent(),
+                    provider, mContext, mMainThreadExecutor.getHandler(),
+                    provider.getRecentsLaunchDuration());
+        }
+
+        protected boolean handleCommand(long elapsedTime) {
+            IconRecentsView recents = mHelper.getVisibleRecentsView();
+            if (recents != null) {
+                recents.handleOverviewCommand();
+                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;
+        }
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/RecentsActivity.java b/go/quickstep/src/com/android/quickstep/RecentsActivity.java
new file mode 100644
index 0000000..7f813ce
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -0,0 +1,74 @@
+/*
+ * 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.app.ActivityOptions;
+import android.view.View;
+
+import com.android.launcher3.R;
+import com.android.launcher3.views.BaseDragLayer;
+import com.android.quickstep.fallback.GoRecentsActivityRootView;
+import com.android.quickstep.views.IconRecentsView;
+
+/**
+ * A recents activity that displays recent tasks with an icon and small snapshot.
+ */
+public final class RecentsActivity extends BaseRecentsActivity {
+
+    private GoRecentsActivityRootView mRecentsRootView;
+    private IconRecentsView mIconRecentsView;
+
+    @Override
+    protected void initViews() {
+        setContentView(R.layout.fallback_recents_activity);
+        mRecentsRootView = findViewById(R.id.drag_layer);
+        mIconRecentsView = findViewById(R.id.overview_panel);
+        mIconRecentsView.setRecentsToActivityHelper(new FallbackRecentsToActivityHelper(this));
+        mIconRecentsView.setShowStatusBarForegroundScrim(true);
+    }
+
+    @Override
+    protected void reapplyUi() {
+        // No-op. Insets are automatically re-applied in the root view.
+    }
+
+    @Override
+    public BaseDragLayer getDragLayer() {
+        return mRecentsRootView;
+    }
+
+    @Override
+    public View getRootView() {
+        return mRecentsRootView;
+    }
+
+    @Override
+    public <T extends View> T getOverviewPanel() {
+        return (T) mIconRecentsView;
+    }
+
+    @Override
+    public ActivityOptions getActivityLaunchOptions(View v) {
+        // Stubbed. Recents launch animation will come from the recents view itself.
+        return null;
+    }
+
+    @Override
+    protected void onResume() {
+        mIconRecentsView.onBeginTransitionToOverview();
+        super.onResume();
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/RecentsToActivityHelper.java b/go/quickstep/src/com/android/quickstep/RecentsToActivityHelper.java
new file mode 100644
index 0000000..8f3b707
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/RecentsToActivityHelper.java
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+/**
+ * Generic interface providing methods to the recents implementation that allow it to callback to
+ * the containing activity.
+ */
+public interface RecentsToActivityHelper {
+
+    /**
+     * The default action to take when leaving/closing recents. In general, this should be used to
+     * go to the appropriate home state.
+     */
+    void leaveRecents();
+}
diff --git a/go/quickstep/src/com/android/quickstep/TaskActionController.java b/go/quickstep/src/com/android/quickstep/TaskActionController.java
new file mode 100644
index 0000000..f49fa3e
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/TaskActionController.java
@@ -0,0 +1,110 @@
+/*
+ * 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.quickstep.TaskAdapter.TASKS_START_POSITION;
+import static com.android.quickstep.TaskUtils.getLaunchComponentKeyForTask;
+
+import android.app.ActivityOptions;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.logging.StatsLogManager;
+import com.android.quickstep.views.TaskItemView;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.Task.TaskKey;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+
+/**
+ * Controller that provides logic for task-related commands on recents and updating the model/view
+ * as appropriate.
+ */
+public final class TaskActionController {
+
+    private final TaskListLoader mLoader;
+    private final TaskAdapter mAdapter;
+    private final StatsLogManager mStatsLogManager;
+
+    public TaskActionController(TaskListLoader loader, TaskAdapter adapter,
+            StatsLogManager logManager) {
+        mLoader = loader;
+        mAdapter = adapter;
+        mStatsLogManager = logManager;
+    }
+
+    /**
+     * Launch the task associated with the task holder, animating into the app from the task view.
+     *
+     * @param viewHolder the task view holder to launch
+     */
+    public void launchTaskFromView(@NonNull TaskHolder viewHolder) {
+        if (!viewHolder.getTask().isPresent()) {
+            return;
+        }
+        TaskItemView itemView = (TaskItemView) (viewHolder.itemView);
+        View v = itemView.getThumbnailView();
+        int left = 0;
+        int top = 0;
+        int width = v.getMeasuredWidth();
+        int height = v.getMeasuredHeight();
+
+        TaskKey key = viewHolder.getTask().get().key;
+        ActivityOptions opts = ActivityOptions.makeClipRevealAnimation(v, left, top, width, height);
+        ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(key, opts,
+                null /* resultCallback */, null /* resultCallbackHandler */);
+        mStatsLogManager.logTaskLaunch(null /* view */, getLaunchComponentKeyForTask(key));
+    }
+
+    /**
+     * Launch the task directly with a basic animation.
+     *
+     * @param task the task to launch
+     */
+    public void launchTask(@NonNull Task task) {
+        ActivityOptions opts = ActivityOptions.makeBasic();
+        ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(task.key, opts,
+                null /* resultCallback */, null /* resultCallbackHandler */);
+        mStatsLogManager.logTaskLaunch(null /* view */, getLaunchComponentKeyForTask(task.key));
+    }
+
+    /**
+     * Removes the task holder and the task, updating the model and the view.
+     *
+     * @param viewHolder the task view holder to remove
+     */
+    public void removeTask(TaskHolder viewHolder) {
+        if (!viewHolder.getTask().isPresent()) {
+            return;
+        }
+        int position = viewHolder.getAdapterPosition();
+        Task task = viewHolder.getTask().get();
+        ActivityManagerWrapper.getInstance().removeTask(task.key.id);
+        mLoader.removeTask(task);
+        mAdapter.notifyItemRemoved(position);
+        // TODO(b/131840601): Add logging point for removal.
+    }
+
+    /**
+     * Clears all tasks and updates the model and view.
+     */
+    public void clearAllTasks() {
+        int count = mAdapter.getItemCount();
+        ActivityManagerWrapper.getInstance().removeAllRecentTasks();
+        mLoader.clearAllTasks();
+        mAdapter.notifyItemRangeRemoved(TASKS_START_POSITION /* positionStart */, count);
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/TaskAdapter.java b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
new file mode 100644
index 0000000..509bf29
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/TaskAdapter.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;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.Button;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView.Adapter;
+import androidx.recyclerview.widget.RecyclerView.ViewHolder;
+
+import com.android.launcher3.R;
+import com.android.quickstep.views.TaskItemView;
+import com.android.systemui.shared.recents.model.Task;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * Recycler view adapter that dynamically inflates and binds {@link TaskHolder} instances with the
+ * appropriate {@link Task} from the recents task list.
+ */
+public final class TaskAdapter extends Adapter<ViewHolder> {
+
+    public static final int CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT = 0;
+    public static final int MAX_TASKS_TO_DISPLAY = 6;
+    public static final int TASKS_START_POSITION = 1;
+
+    public static final int ITEM_TYPE_TASK = 0;
+    public static final int ITEM_TYPE_CLEAR_ALL = 1;
+
+    private static final String TAG = "TaskAdapter";
+    private final TaskListLoader mLoader;
+    private TaskActionController mTaskActionController;
+    private OnClickListener mClearAllListener;
+    private boolean mIsShowingLoadingUi;
+
+    public TaskAdapter(@NonNull TaskListLoader loader) {
+        mLoader = loader;
+    }
+
+    public void setActionController(TaskActionController taskActionController) {
+        mTaskActionController = taskActionController;
+    }
+
+    public void setOnClearAllClickListener(OnClickListener listener) {
+        mClearAllListener = listener;
+    }
+
+    /**
+     * Sets all positions in the task adapter to loading views, binding new views if necessary.
+     * This changes the task adapter's view of the data, so the appropriate notify events should be
+     * called in addition to this method to reflect the changes.
+     *
+     * @param isShowingLoadingUi true to bind loading task views to all positions, false to return
+     *                           to the real data
+     */
+    public void setIsShowingLoadingUi(boolean isShowingLoadingUi) {
+        mIsShowingLoadingUi = isShowingLoadingUi;
+    }
+
+    @Override
+    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        switch (viewType) {
+            case ITEM_TYPE_TASK:
+                TaskItemView itemView = (TaskItemView) LayoutInflater.from(parent.getContext())
+                        .inflate(R.layout.task_item_view, parent, false);
+                TaskHolder taskHolder = new TaskHolder(itemView);
+                itemView.setOnClickListener(
+                        view -> mTaskActionController.launchTaskFromView(taskHolder));
+                return taskHolder;
+            case ITEM_TYPE_CLEAR_ALL:
+                View clearView = LayoutInflater.from(parent.getContext())
+                        .inflate(R.layout.clear_all_button, parent, false);
+                ClearAllHolder clearAllHolder = new ClearAllHolder(clearView);
+                Button clearViewButton = clearView.findViewById(R.id.clear_all_button);
+                clearViewButton.setOnClickListener(mClearAllListener);
+                return clearAllHolder;
+            default:
+                throw new IllegalArgumentException("No known holder for item type: " + viewType);
+        }
+    }
+
+    @Override
+    public void onBindViewHolder(ViewHolder holder, int position) {
+        onBindViewHolderInternal(holder, position, false /* willAnimate */);
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull ViewHolder holder, int position,
+            @NonNull List<Object> payloads) {
+        if (payloads.isEmpty()) {
+            super.onBindViewHolder(holder, position, payloads);
+            return;
+        }
+        int changeType = (int) payloads.get(0);
+        if (changeType == CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT) {
+            // Bind in preparation for animation
+            onBindViewHolderInternal(holder, position, true /* willAnimate */);
+        } else {
+            throw new IllegalArgumentException("Payload content is not a valid change event type: "
+                    + changeType);
+        }
+    }
+
+    private void onBindViewHolderInternal(@NonNull ViewHolder holder, int position,
+            boolean willAnimate) {
+        int itemType = getItemViewType(position);
+        switch (itemType) {
+            case ITEM_TYPE_TASK:
+                TaskHolder taskHolder = (TaskHolder) holder;
+                if (mIsShowingLoadingUi) {
+                    taskHolder.bindEmptyUi();
+                    return;
+                }
+                List<Task> tasks = mLoader.getCurrentTaskList();
+                int taskPos = position - TASKS_START_POSITION;
+                if (taskPos >= tasks.size()) {
+                    // Task list has updated.
+                    return;
+                }
+                Task task = tasks.get(taskPos);
+                taskHolder.bindTask(task, willAnimate /* willAnimate */);
+                mLoader.loadTaskIconAndLabel(task, () -> {
+                    // Ensure holder still has the same task.
+                    if (Objects.equals(Optional.of(task), taskHolder.getTask())) {
+                        taskHolder.getTaskItemView().setIcon(task.icon);
+                        taskHolder.getTaskItemView().setLabel(task.titleDescription);
+                    }
+                });
+                mLoader.loadTaskThumbnail(task, () -> {
+                    if (Objects.equals(Optional.of(task), taskHolder.getTask())) {
+                        taskHolder.getTaskItemView().setThumbnail(task.thumbnail);
+                    }
+                });
+                break;
+            case ITEM_TYPE_CLEAR_ALL:
+                // Nothing to bind.
+                break;
+            default:
+                throw new IllegalArgumentException("No known holder for item type: " + itemType);
+        }
+    }
+
+    @Override
+    public int getItemViewType(int position) {
+        // Bottom is always clear all button.
+        return (position == 0) ? ITEM_TYPE_CLEAR_ALL : ITEM_TYPE_TASK;
+    }
+
+    @Override
+    public int getItemCount() {
+        int itemCount = TASKS_START_POSITION;
+        if (mIsShowingLoadingUi) {
+            // Show loading version of all items.
+            itemCount += MAX_TASKS_TO_DISPLAY;
+        } else {
+            itemCount += Math.min(mLoader.getCurrentTaskList().size(), MAX_TASKS_TO_DISPLAY);
+        }
+        return itemCount;
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/TaskHolder.java b/go/quickstep/src/com/android/quickstep/TaskHolder.java
new file mode 100644
index 0000000..49b6aaa
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/TaskHolder.java
@@ -0,0 +1,85 @@
+/*
+ * 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 androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView.ViewHolder;
+
+import com.android.quickstep.views.TaskItemView;
+import com.android.systemui.shared.recents.model.Task;
+
+import java.util.Optional;
+
+/**
+ * A recycler view holder that holds the task view and binds {@link Task} content (app title, icon,
+ * etc.) to the view.
+ */
+public final class TaskHolder extends ViewHolder {
+
+    private final TaskItemView mTaskItemView;
+    private Task mTask;
+
+    public TaskHolder(TaskItemView itemView) {
+        super(itemView);
+        mTaskItemView = itemView;
+    }
+
+    public TaskItemView getTaskItemView() {
+        return mTaskItemView;
+    }
+
+    /**
+     * Bind the task model to the holder. This will take the current task content in the task
+     * object (i.e. icon, thumbnail, label) and either apply the content immediately or simply bind
+     * the content to animate to at a later time. If the task does not have all its content loaded,
+     * the view will prepare appropriate default placeholders and it is the callers responsibility
+     * to change them at a later time.
+     *
+     * Regardless of whether it is animating, input handlers will be bound immediately (see
+     * {@link TaskActionController}).
+     *
+     * @param task the task to bind to the view
+     * @param willAnimate true if UI should animate in later, false if it should apply immediately
+     */
+    public void bindTask(@NonNull Task task, boolean willAnimate) {
+        mTask = task;
+        if (willAnimate) {
+            mTaskItemView.startContentAnimation(task.icon, task.thumbnail, task.titleDescription);
+        } else {
+            mTaskItemView.setIcon(task.icon);
+            mTaskItemView.setThumbnail(task.thumbnail);
+            mTaskItemView.setLabel(task.titleDescription);
+        }
+    }
+
+    /**
+     * Bind a generic empty UI to the holder to make it clear that the item is loading/unbound and
+     * should not be expected to react to user input.
+     */
+    public void bindEmptyUi() {
+        mTask = null;
+        mTaskItemView.resetToEmptyUi();
+    }
+
+    /**
+     * Gets the task currently bound to this view. May be null if task holder is in a loading state.
+     *
+     * @return the current task
+     */
+    public Optional<Task> getTask() {
+        return Optional.ofNullable(mTask);
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/TaskListLoader.java b/go/quickstep/src/com/android/quickstep/TaskListLoader.java
new file mode 100644
index 0000000..1335cac
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/TaskListLoader.java
@@ -0,0 +1,140 @@
+/*
+ * 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.content.Context;
+
+import androidx.annotation.Nullable;
+
+import com.android.systemui.shared.recents.model.Task;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * This class is responsible for maintaining the list of tasks and the task content. The list must
+ * be updated explicitly with {@link #loadTaskList} whenever the list needs to be
+ * up-to-date.
+ */
+public final class TaskListLoader {
+
+    private final RecentsModel mRecentsModel;
+
+    private ArrayList<Task> mTaskList = new ArrayList<>();
+    private int mTaskListChangeId;
+
+    public TaskListLoader(Context context) {
+        mRecentsModel = RecentsModel.INSTANCE.get(context);
+    }
+
+    /**
+     * Returns the current task list as of the last completed load (see {@link #loadTaskList}) as a
+     * read-only list. This list of tasks is not guaranteed to have all content loaded.
+     *
+     * @return the current list of tasks
+     */
+    public List<Task> getCurrentTaskList() {
+        return Collections.unmodifiableList(mTaskList);
+    }
+
+    /**
+     * Whether or not the loader needs to load data to be up to date. This can return true if the
+     * task list is already up to date OR there is already a load in progress for the task list to
+     * become up to date.
+     *
+     * @return true if already up to date or load in progress, false otherwise
+     */
+    public boolean needsToLoad() {
+        return !mRecentsModel.isTaskListValid(mTaskListChangeId);
+    }
+
+    /**
+     * Fetches the most recent tasks and updates the task list asynchronously. This call does not
+     * provide guarantees the task content (icon, thumbnail, label) are loaded but will fill in
+     * what it has. May run the callback immediately if there have been no changes in the task
+     * list since the start of the last load.
+     *
+     * @param onLoadedCallback callback to run when task list is loaded
+     */
+    public void loadTaskList(@Nullable Consumer<ArrayList<Task>> onLoadedCallback) {
+        if (!needsToLoad()) {
+            if (onLoadedCallback != null) {
+                onLoadedCallback.accept(mTaskList);
+            }
+            return;
+        }
+        // TODO: Look into error checking / more robust handling for when things go wrong.
+        mTaskListChangeId = mRecentsModel.getTasks(loadedTasks -> {
+            ArrayList<Task> tasks = new ArrayList<>(loadedTasks);
+            // Reverse tasks to put most recent at the bottom of the view
+            Collections.reverse(tasks);
+            // Load task content
+            for (Task task : tasks) {
+                int loadedPos = mTaskList.indexOf(task);
+                if (loadedPos == -1) {
+                    continue;
+                }
+                Task loadedTask = mTaskList.get(loadedPos);
+                task.icon = loadedTask.icon;
+                task.titleDescription = loadedTask.titleDescription;
+                task.thumbnail = loadedTask.thumbnail;
+            }
+            mTaskList = tasks;
+            onLoadedCallback.accept(tasks);
+        });
+    }
+
+    /**
+     * Load task icon and label asynchronously if it is not already loaded in the task. If the task
+     * already has an icon, this calls the callback immediately.
+     *
+     * @param task task to update with icon + label
+     * @param onLoadedCallback callback to run when task has icon and label
+     */
+    public void loadTaskIconAndLabel(Task task, @Nullable Runnable onLoadedCallback) {
+        mRecentsModel.getIconCache().updateIconInBackground(task,
+                loadedTask -> onLoadedCallback.run());
+    }
+
+    /**
+     * Load thumbnail asynchronously if not already loaded in the task. If the task already has a
+     * thumbnail or if the thumbnail is cached, this calls the callback immediately.
+     *
+     * @param task task to update with the thumbnail
+     * @param onLoadedCallback callback to run when task has thumbnail
+     */
+    public void loadTaskThumbnail(Task task, @Nullable Runnable onLoadedCallback) {
+        mRecentsModel.getThumbnailCache().updateThumbnailInBackground(task,
+                thumbnail -> onLoadedCallback.run());
+    }
+
+    /**
+     * Removes the task from the current task list.
+     */
+    void removeTask(Task task) {
+        mTaskList.remove(task);
+    }
+
+    /**
+     * Clears the current task list.
+     */
+    void clearAllTasks() {
+        mTaskList.clear();
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/TaskSwipeCallback.java b/go/quickstep/src/com/android/quickstep/TaskSwipeCallback.java
new file mode 100644
index 0000000..57f49d6
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/TaskSwipeCallback.java
@@ -0,0 +1,77 @@
+/*
+ * 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 androidx.recyclerview.widget.ItemTouchHelper.RIGHT;
+
+import static com.android.quickstep.TaskAdapter.ITEM_TYPE_CLEAR_ALL;
+
+import android.graphics.Canvas;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.ItemTouchHelper;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.ViewHolder;
+
+import java.util.function.Consumer;
+
+/**
+ * Callback for swipe input on {@link TaskHolder} views in the recents view.
+ */
+public final class TaskSwipeCallback extends ItemTouchHelper.SimpleCallback {
+
+    private final Consumer<TaskHolder> mOnTaskSwipeCallback;
+
+    public TaskSwipeCallback(Consumer<TaskHolder> onTaskSwipeCallback) {
+        super(0 /* dragDirs */, RIGHT);
+        mOnTaskSwipeCallback = onTaskSwipeCallback;
+    }
+
+    @Override
+    public boolean onMove(RecyclerView recyclerView, ViewHolder viewHolder,
+            ViewHolder target) {
+        return false;
+    }
+
+    @Override
+    public void onSwiped(ViewHolder viewHolder, int direction) {
+        if (direction == RIGHT) {
+            mOnTaskSwipeCallback.accept((TaskHolder) viewHolder);
+        }
+    }
+
+    @Override
+    public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView,
+            @NonNull ViewHolder viewHolder, float dX, float dY, int actionState,
+            boolean isCurrentlyActive) {
+        if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
+            float alpha = 1.0f - dX / (float) viewHolder.itemView.getWidth();
+            viewHolder.itemView.setAlpha(alpha);
+        }
+        super.onChildDraw(c, recyclerView, viewHolder, dX, dY,
+                    actionState, isCurrentlyActive);
+    }
+
+    @Override
+    public int getSwipeDirs(@NonNull RecyclerView recyclerView,
+            @NonNull ViewHolder viewHolder) {
+        if (viewHolder.getItemViewType() == ITEM_TYPE_CLEAR_ALL) {
+            // Clear all button should not be swipable.
+            return 0;
+        }
+        return super.getSwipeDirs(recyclerView, viewHolder);
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/ThumbnailDrawable.java b/go/quickstep/src/com/android/quickstep/ThumbnailDrawable.java
new file mode 100644
index 0000000..922e68a
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/ThumbnailDrawable.java
@@ -0,0 +1,145 @@
+/*
+ * 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.graphics.Shader.TileMode.CLAMP;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.R;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+
+/**
+ * Bitmap backed drawable that supports rotating the thumbnail bitmap depending on if the
+ * orientation the thumbnail was taken in matches the desired orientation. In addition, the
+ * thumbnail always fills into the containing bounds.
+ */
+public final class ThumbnailDrawable extends Drawable {
+
+    private final Paint mPaint = new Paint();
+    private final Matrix mMatrix = new Matrix();
+    private final ThumbnailData mThumbnailData;
+    private final BitmapShader mShader;
+    private final RectF mDestRect = new RectF();
+    private final int mCornerRadius;
+    private int mRequestedOrientation;
+
+    public ThumbnailDrawable(Resources res, @NonNull ThumbnailData thumbnailData,
+            int requestedOrientation) {
+        mThumbnailData = thumbnailData;
+        mRequestedOrientation = requestedOrientation;
+        mCornerRadius = (int) res.getDimension(R.dimen.task_thumbnail_corner_radius);
+        mShader = new BitmapShader(mThumbnailData.thumbnail, CLAMP, CLAMP);
+        mPaint.setShader(mShader);
+        mPaint.setAntiAlias(true);
+        updateMatrix();
+    }
+
+    /**
+     * Set the requested orientation.
+     *
+     * @param orientation the orientation we want the thumbnail to be in
+     */
+    public void setRequestedOrientation(int orientation) {
+        if (mRequestedOrientation != orientation) {
+            mRequestedOrientation = orientation;
+            updateMatrix();
+        }
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        if (mThumbnailData.thumbnail == null) {
+            return;
+        }
+        canvas.drawRoundRect(mDestRect, mCornerRadius, mCornerRadius, mPaint);
+    }
+
+    @Override
+    protected void onBoundsChange(Rect bounds) {
+        super.onBoundsChange(bounds);
+        mDestRect.set(bounds);
+        updateMatrix();
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        final int oldAlpha = mPaint.getAlpha();
+        if (alpha != oldAlpha) {
+            mPaint.setAlpha(alpha);
+            invalidateSelf();
+        }
+    }
+
+    @Override
+    public int getAlpha() {
+        return mPaint.getAlpha();
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter colorFilter) {
+        mPaint.setColorFilter(colorFilter);
+        invalidateSelf();
+    }
+
+    @Override
+    public ColorFilter getColorFilter() {
+        return mPaint.getColorFilter();
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.TRANSLUCENT;
+    }
+
+    private void updateMatrix() {
+        if (mThumbnailData.thumbnail == null) {
+            return;
+        }
+        mMatrix.reset();
+        float scaleX;
+        float scaleY;
+        Rect bounds = getBounds();
+        Bitmap thumbnail = mThumbnailData.thumbnail;
+        if (mRequestedOrientation != mThumbnailData.orientation) {
+            // Rotate and translate so that top left is the same.
+            mMatrix.postRotate(90, 0, 0);
+            mMatrix.postTranslate(thumbnail.getHeight(), 0);
+
+            scaleX = (float) bounds.width() / thumbnail.getHeight();
+            scaleY = (float) bounds.height() / thumbnail.getWidth();
+        } else {
+            scaleX = (float) bounds.width() / thumbnail.getWidth();
+            scaleY = (float) bounds.height() / thumbnail.getHeight();
+        }
+        // Scale to fill.
+        mMatrix.postScale(scaleX, scaleY);
+        mShader.setLocalMatrix(mMatrix);
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/TouchInteractionService.java b/go/quickstep/src/com/android/quickstep/TouchInteractionService.java
new file mode 100644
index 0000000..900b94e
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -0,0 +1,183 @@
+/*
+ * 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.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
+
+import android.annotation.TargetApi;
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.Region;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Process;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.MotionEvent;
+
+import com.android.launcher3.Utilities;
+import com.android.launcher3.compat.UserManagerCompat;
+import com.android.systemui.shared.recents.IOverviewProxy;
+import com.android.systemui.shared.recents.ISystemUiProxy;
+
+/**
+ * Service connected by system-UI for handling touch interaction.
+ */
+@TargetApi(Build.VERSION_CODES.O)
+public class TouchInteractionService extends Service {
+
+    private static final String TAG = "GoTouchInteractionService";
+    private boolean mIsUserUnlocked;
+    private BroadcastReceiver mUserUnlockedReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
+                initWhenUserUnlocked();
+            }
+        }
+    };
+
+    private final IBinder mMyBinder = new IOverviewProxy.Stub() {
+
+        @Override
+        public void onActiveNavBarRegionChanges(Region region) throws RemoteException { }
+
+        @Override
+        public void onInitialize(Bundle bundle) throws RemoteException {
+            ISystemUiProxy iSystemUiProxy = ISystemUiProxy.Stub
+                    .asInterface(bundle.getBinder(KEY_EXTRA_SYSUI_PROXY));
+            mRecentsModel.setSystemUiProxy(iSystemUiProxy);
+        }
+
+        @Override
+        public void onOverviewToggle() {
+            if (mIsUserUnlocked) {
+                mOverviewCommandHelper.onOverviewToggle();
+            }
+        }
+
+        @Override
+        public void onOverviewShown(boolean triggeredFromAltTab) {
+            if (mIsUserUnlocked) {
+                mOverviewCommandHelper.onOverviewShown(triggeredFromAltTab);
+            }
+        }
+
+        @Override
+        public void onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
+            if (mIsUserUnlocked && triggeredFromAltTab && !triggeredFromHomeKey) {
+                // onOverviewShownFromAltTab hides the overview and ends at the target app
+                mOverviewCommandHelper.onOverviewHidden();
+            }
+        }
+
+        @Override
+        public void onTip(int actionType, int viewType) {
+            if (mIsUserUnlocked) {
+                mOverviewCommandHelper.onTip(actionType, viewType);
+            }
+        }
+
+        @Override
+        public void onAssistantAvailable(boolean available) {
+            // TODO handle assistant
+        }
+
+        @Override
+        public void onAssistantVisibilityChanged(float visibility) {
+            // TODO handle assistant
+        }
+
+        public void onBackAction(boolean completed, int downX, int downY, boolean isButton,
+                boolean gestureSwipeLeft) {
+        }
+
+        public void onSystemUiStateChanged(int stateFlags) {
+            // To be implemented
+        }
+
+        /** 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) { }
+
+        public void onBind(ISystemUiProxy iSystemUiProxy) {
+            mRecentsModel.setSystemUiProxy(iSystemUiProxy);
+        }
+    };
+
+    private static boolean sConnected = false;;
+
+    public static boolean isConnected() {
+        return sConnected;
+    }
+
+    private RecentsModel mRecentsModel;
+    private OverviewComponentObserver mOverviewComponentObserver;
+    private OverviewCommandHelper mOverviewCommandHelper;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        if (UserManagerCompat.getInstance(this).isUserUnlocked(Process.myUserHandle())) {
+            initWhenUserUnlocked();
+        } else {
+            mIsUserUnlocked = false;
+            registerReceiver(mUserUnlockedReceiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED));
+        }
+
+        sConnected = true;
+    }
+
+    private void initWhenUserUnlocked() {
+        mRecentsModel = RecentsModel.INSTANCE.get(this);
+        mOverviewComponentObserver = new OverviewComponentObserver(this);
+        mOverviewCommandHelper = new OverviewCommandHelper(this,
+                mOverviewComponentObserver);
+        mIsUserUnlocked = true;
+        Utilities.unregisterReceiverSafely(this, mUserUnlockedReceiver);
+    }
+
+    @Override
+    public void onDestroy() {
+        if (mIsUserUnlocked) {
+            mOverviewComponentObserver.onDestroy();
+        }
+        Utilities.unregisterReceiverSafely(this, mUserUnlockedReceiver);
+        sConnected = false;
+        super.onDestroy();
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "Touch service connected");
+        }
+        return mMyBinder;
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/fallback/GoRecentsActivityRootView.java b/go/quickstep/src/com/android/quickstep/fallback/GoRecentsActivityRootView.java
new file mode 100644
index 0000000..b550011
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/fallback/GoRecentsActivityRootView.java
@@ -0,0 +1,55 @@
+/*
+ * 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.fallback;
+
+import android.content.Context;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.WindowInsets;
+
+import com.android.launcher3.util.TouchController;
+import com.android.launcher3.views.BaseDragLayer;
+import com.android.quickstep.RecentsActivity;
+
+/**
+ * Minimal implementation of {@link BaseDragLayer} for Go's fallback recents activity.
+ */
+public final class GoRecentsActivityRootView extends BaseDragLayer<RecentsActivity> {
+    public GoRecentsActivityRootView(Context context, AttributeSet attrs) {
+        super(context, attrs, 1 /* alphaChannelCount */);
+        // Go leaves touch control to the view itself.
+        mControllers = new TouchController[0];
+        setSystemUiVisibility(SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+                | SYSTEM_UI_FLAG_LAYOUT_STABLE);
+    }
+
+    @Override
+    public void setInsets(Rect insets) {
+        if (insets.equals(mInsets)) {
+            return;
+        }
+        super.setInsets(insets);
+    }
+
+    @Override
+    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+        Insets sysInsets = insets.getSystemWindowInsets();
+        setInsets(new Rect(sysInsets.left, sysInsets.top, sysInsets.right, sysInsets.bottom));
+        return insets.consumeSystemWindowInsets();
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
new file mode 100644
index 0000000..87b4d4e
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
@@ -0,0 +1,942 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.views;
+
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+
+import static androidx.recyclerview.widget.LinearLayoutManager.VERTICAL;
+
+import static com.android.launcher3.anim.Interpolators.ACCEL_2;
+import static com.android.quickstep.TaskAdapter.CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT;
+import static com.android.quickstep.TaskAdapter.ITEM_TYPE_CLEAR_ALL;
+import static com.android.quickstep.TaskAdapter.ITEM_TYPE_TASK;
+import static com.android.quickstep.TaskAdapter.MAX_TASKS_TO_DISPLAY;
+import static com.android.quickstep.TaskAdapter.TASKS_START_POSITION;
+import static com.android.quickstep.util.RemoteAnimationProvider.getLayer;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+import android.util.ArraySet;
+import android.util.AttributeSet;
+import android.util.FloatProperty;
+import android.view.View;
+import android.view.ViewDebug;
+import android.view.ViewTreeObserver;
+import android.view.animation.PathInterpolator;
+import android.widget.FrameLayout;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.interpolator.view.animation.LinearOutSlowInInterpolator;
+import androidx.recyclerview.widget.DefaultItemAnimator;
+import androidx.recyclerview.widget.ItemTouchHelper;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver;
+import androidx.recyclerview.widget.RecyclerView.ItemDecoration;
+import androidx.recyclerview.widget.RecyclerView.OnChildAttachStateChangeListener;
+
+import com.android.launcher3.BaseActivity;
+import com.android.launcher3.Insettable;
+import com.android.launcher3.R;
+import com.android.launcher3.util.Themes;
+import com.android.quickstep.ContentFillItemAnimator;
+import com.android.quickstep.RecentsModel;
+import com.android.quickstep.RecentsToActivityHelper;
+import com.android.quickstep.TaskActionController;
+import com.android.quickstep.TaskAdapter;
+import com.android.quickstep.TaskHolder;
+import com.android.quickstep.TaskListLoader;
+import com.android.quickstep.TaskSwipeCallback;
+import com.android.quickstep.util.MultiValueUpdateListener;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * Root view for the icon recents view. Acts as the main interface to the rest of the Launcher code
+ * base.
+ */
+public final class IconRecentsView extends FrameLayout implements Insettable {
+
+    public static final FloatProperty<IconRecentsView> CONTENT_ALPHA =
+            new FloatProperty<IconRecentsView>("contentAlpha") {
+                @Override
+                public void setValue(IconRecentsView view, float v) {
+                    ALPHA.set(view, v);
+                    if (view.getVisibility() != VISIBLE && v > 0) {
+                        view.setVisibility(VISIBLE);
+                    } else if (view.getVisibility() != GONE && v == 0){
+                        view.setVisibility(GONE);
+                    }
+                }
+
+                @Override
+                public Float get(IconRecentsView view) {
+                    return ALPHA.get(view);
+                }
+            };
+    private static final long CROSSFADE_DURATION = 300;
+    private static final long LAYOUT_ITEM_ANIMATE_IN_DURATION = 150;
+    private static final long LAYOUT_ITEM_ANIMATE_IN_DELAY_BETWEEN = 40;
+    private static final long ITEM_ANIMATE_OUT_DURATION = 150;
+    private static final long ITEM_ANIMATE_OUT_DELAY_BETWEEN = 40;
+    private static final float ITEM_ANIMATE_OUT_TRANSLATION_X_RATIO = .25f;
+    private static final long CLEAR_ALL_FADE_DELAY = 120;
+
+    private static final long REMOTE_TO_RECENTS_APP_SCALE_DOWN_DURATION = 300;
+    private static final long REMOTE_TO_RECENTS_VERTICAL_EASE_IN_DURATION = 400;
+    private static final long REMOTE_TO_RECENTS_ITEM_FADE_START_DELAY = 200;
+    private static final long REMOTE_TO_RECENTS_ITEM_FADE_DURATION = 217;
+    private static final long REMOTE_TO_RECENTS_ITEM_FADE_BETWEEN_DELAY = 33;
+
+    private static final PathInterpolator FAST_OUT_SLOW_IN_1 =
+            new PathInterpolator(.4f, 0f, 0f, 1f);
+    private static final PathInterpolator FAST_OUT_SLOW_IN_2 =
+            new PathInterpolator(.5f, 0f, 0f, 1f);
+    private static final LinearOutSlowInInterpolator OUT_SLOW_IN =
+            new LinearOutSlowInInterpolator();
+
+    public static final long REMOTE_APP_TO_OVERVIEW_DURATION =
+            REMOTE_TO_RECENTS_VERTICAL_EASE_IN_DURATION;
+
+    /**
+     * A ratio representing the view's relative placement within its padded space. For example, 0
+     * is top aligned and 0.5 is centered vertically.
+     */
+    @ViewDebug.ExportedProperty(category = "launcher")
+
+    private final Context mContext;
+    private final TaskListLoader mTaskLoader;
+    private final TaskAdapter mTaskAdapter;
+    private final LinearLayoutManager mTaskLayoutManager;
+    private final TaskActionController mTaskActionController;
+    private final DefaultItemAnimator mDefaultItemAnimator = new DefaultItemAnimator();
+    private final ContentFillItemAnimator mLoadingContentItemAnimator =
+            new ContentFillItemAnimator();
+    private final BaseActivity mActivity;
+    private final Drawable mStatusBarForegroundScrim;
+
+    private RecentsToActivityHelper mActivityHelper;
+    private RecyclerView mTaskRecyclerView;
+    private View mShowingContentView;
+    private View mEmptyView;
+    private View mContentView;
+    private boolean mTransitionedFromApp;
+    private boolean mUsingRemoteAnimation;
+    private boolean mStartedEnterAnimation;
+    private boolean mShowStatusBarForegroundScrim;
+    private AnimatorSet mLayoutAnimation;
+    private final ArraySet<View> mLayingOutViews = new ArraySet<>();
+    private Rect mInsets;
+    private final RecentsModel.TaskThumbnailChangeListener listener = (taskId, thumbnailData) -> {
+        ArrayList<TaskItemView> itemViews = getTaskViews();
+        for (int i = 0, size = itemViews.size(); i < size; i++) {
+            TaskItemView taskView = itemViews.get(i);
+            TaskHolder taskHolder = (TaskHolder) mTaskRecyclerView.getChildViewHolder(taskView);
+            Optional<Task> optTask = taskHolder.getTask();
+            if (optTask.filter(task -> task.key.id == taskId).isPresent()) {
+                Task task = optTask.get();
+                // Update thumbnail on the task.
+                task.thumbnail = thumbnailData;
+                taskView.setThumbnail(thumbnailData);
+                return task;
+            }
+        }
+        return null;
+    };
+
+    public IconRecentsView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mActivity = BaseActivity.fromContext(context);
+        mContext = context;
+        mStatusBarForegroundScrim  =
+                Themes.getAttrDrawable(mContext, R.attr.workspaceStatusBarScrim);
+        mTaskLoader = new TaskListLoader(mContext);
+        mTaskAdapter = new TaskAdapter(mTaskLoader);
+        mTaskAdapter.setOnClearAllClickListener(view -> animateClearAllTasks());
+        mTaskActionController = new TaskActionController(mTaskLoader, mTaskAdapter,
+                mActivity.getStatsLogManager());
+        mTaskAdapter.setActionController(mTaskActionController);
+        mTaskLayoutManager = new LinearLayoutManager(mContext, VERTICAL, true /* reverseLayout */);
+        RecentsModel.INSTANCE.get(context).addThumbnailChangeListener(listener);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        if (mTaskRecyclerView == null) {
+            mTaskRecyclerView = findViewById(R.id.recent_task_recycler_view);
+            mTaskRecyclerView.setAdapter(mTaskAdapter);
+            mTaskRecyclerView.setLayoutManager(mTaskLayoutManager);
+            ItemTouchHelper helper = new ItemTouchHelper(
+                    new TaskSwipeCallback(holder -> {
+                        mTaskActionController.removeTask(holder);
+                        if (mTaskLoader.getCurrentTaskList().isEmpty()) {
+                            mActivityHelper.leaveRecents();
+                        }
+                    }));
+            helper.attachToRecyclerView(mTaskRecyclerView);
+            mTaskRecyclerView.addOnChildAttachStateChangeListener(
+                    new OnChildAttachStateChangeListener() {
+                        @Override
+                        public void onChildViewAttachedToWindow(@NonNull View view) {
+                            if (mLayoutAnimation != null && !mLayingOutViews.contains(view)) {
+                                // Child view was added that is not part of current layout animation
+                                // so restart the animation.
+                                animateFadeInLayoutAnimation();
+                            }
+                        }
+
+                        @Override
+                        public void onChildViewDetachedFromWindow(@NonNull View view) { }
+                    });
+            mTaskRecyclerView.setItemAnimator(mDefaultItemAnimator);
+            mLoadingContentItemAnimator.setOnAnimationFinishedRunnable(
+                    () -> mTaskRecyclerView.setItemAnimator(new DefaultItemAnimator()));
+            ItemDecoration marginDecorator = new ItemDecoration() {
+                @Override
+                public void getItemOffsets(@NonNull Rect outRect, @NonNull View view,
+                        @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
+                    // TODO: Determine if current margins cause off screen item to be fully off
+                    // screen and if so, modify them so that it is partially off screen.
+                    int itemType = parent.getChildViewHolder(view).getItemViewType();
+                    Resources res = getResources();
+                    switch (itemType) {
+                        case ITEM_TYPE_CLEAR_ALL:
+                            outRect.top = (int) res.getDimension(
+                                    R.dimen.clear_all_item_view_top_margin);
+                            outRect.bottom = (int) res.getDimension(
+                                    R.dimen.clear_all_item_view_bottom_margin);
+                            break;
+                        case ITEM_TYPE_TASK:
+                            int desiredTopMargin = (int) res.getDimension(
+                                    R.dimen.task_item_top_margin);
+                            if (mTaskRecyclerView.getChildAdapterPosition(view) ==
+                                    state.getItemCount() - 1) {
+                                // Only add top margin to top task view if insets aren't enough.
+                                if (mInsets.top < desiredTopMargin) {
+                                    outRect.top = desiredTopMargin - mInsets.bottom;
+                                }
+                                return;
+                            }
+                            outRect.top = desiredTopMargin;
+                            break;
+                        default:
+                    }
+                }
+            };
+            mTaskRecyclerView.addItemDecoration(marginDecorator);
+
+            mEmptyView = findViewById(R.id.recent_task_empty_view);
+            mContentView = mTaskRecyclerView;
+            mTaskAdapter.registerAdapterDataObserver(new AdapterDataObserver() {
+                @Override
+                public void onChanged() {
+                    updateContentViewVisibility();
+                }
+
+                @Override
+                public void onItemRangeRemoved(int positionStart, int itemCount) {
+                    updateContentViewVisibility();
+                }
+            });
+        }
+    }
+
+    @Override
+    public void setEnabled(boolean enabled) {
+        super.setEnabled(enabled);
+        int childCount = mTaskRecyclerView.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            mTaskRecyclerView.getChildAt(i).setEnabled(enabled);
+        }
+    }
+
+    /**
+     * Set activity helper for the view to callback to.
+     *
+     * @param helper the activity helper
+     */
+    public void setRecentsToActivityHelper(@NonNull RecentsToActivityHelper helper) {
+        mActivityHelper = helper;
+    }
+
+    /**
+     * Logic for when we know we are going to overview/recents and will be putting up the recents
+     * view. This should be used to prepare recents (e.g. load any task data, etc.) before it
+     * becomes visible.
+     */
+    public void onBeginTransitionToOverview() {
+        mStartedEnterAnimation = false;
+        if (mContext.getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE) {
+            // Scroll to bottom of task in landscape mode. This is a non-issue in portrait mode as
+            // all tasks should be visible to fill up the screen in portrait mode and the view will
+            // not be scrollable.
+            mTaskLayoutManager.scrollToPositionWithOffset(TASKS_START_POSITION, 0 /* offset */);
+        }
+        if (!mUsingRemoteAnimation) {
+            scheduleFadeInLayoutAnimation();
+        }
+        // Load any task changes
+        if (!mTaskLoader.needsToLoad()) {
+            return;
+        }
+        mTaskAdapter.setIsShowingLoadingUi(true);
+        mTaskAdapter.notifyDataSetChanged();
+        mTaskLoader.loadTaskList(tasks -> {
+            int numEmptyItems = mTaskAdapter.getItemCount() - TASKS_START_POSITION;
+            mTaskAdapter.setIsShowingLoadingUi(false);
+            int numActualItems = mTaskAdapter.getItemCount() - TASKS_START_POSITION;
+            if (numEmptyItems < numActualItems) {
+                throw new IllegalStateException("There are less empty item views than the number "
+                        + "of items to animate to.");
+            }
+            // Possible that task list loads faster than adapter changes propagate to layout so
+            // only start content fill animation if there aren't any pending adapter changes and
+            // we've started the on enter layout animation.
+            boolean needsContentFillAnimation =
+                    !mTaskRecyclerView.hasPendingAdapterUpdates() && mStartedEnterAnimation;
+            if (needsContentFillAnimation) {
+                // Set item animator for content filling animation. The item animator will switch
+                // back to the default on completion
+                mTaskRecyclerView.setItemAnimator(mLoadingContentItemAnimator);
+                mTaskAdapter.notifyItemRangeRemoved(TASKS_START_POSITION + numActualItems,
+                        numEmptyItems - numActualItems);
+                mTaskAdapter.notifyItemRangeChanged(TASKS_START_POSITION, numActualItems,
+                        CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT);
+            } else {
+                // Notify change without animating.
+                mTaskAdapter.notifyDataSetChanged();
+            }
+        });
+    }
+
+    /**
+     * Set whether we transitioned to recents from the most recent app.
+     *
+     * @param transitionedFromApp true if transitioned from the most recent app, false otherwise
+     */
+    public void setTransitionedFromApp(boolean transitionedFromApp) {
+        mTransitionedFromApp = transitionedFromApp;
+    }
+
+    /**
+     * Set whether we're using a custom remote animation. If so, we will not do the default layout
+     * animation when entering recents and instead wait for the remote app surface to be ready to
+     * use.
+     *
+     * @param usingRemoteAnimation true if doing a remote animation, false o/w
+     */
+    public void setUsingRemoteAnimation(boolean usingRemoteAnimation) {
+        mUsingRemoteAnimation = usingRemoteAnimation;
+    }
+
+    /**
+     * Handles input from the overview button. Launch the most recent task unless we just came from
+     * the app. In that case, we launch the next most recent.
+     */
+    public void handleOverviewCommand() {
+        List<Task> tasks = mTaskLoader.getCurrentTaskList();
+        int tasksSize = tasks.size();
+        if (tasksSize == 0) {
+            // Do nothing
+            return;
+        }
+        Task taskToLaunch;
+        if (mTransitionedFromApp && tasksSize > 1) {
+            // Launch the next most recent app
+            taskToLaunch = tasks.get(1);
+        } else {
+            // Launch the most recent app
+            taskToLaunch = tasks.get(0);
+        }
+
+        // See if view for this task is attached, and if so, animate launch from that view.
+        ArrayList<TaskItemView> itemViews = getTaskViews();
+        for (int i = 0, size = itemViews.size(); i < size; i++) {
+            TaskItemView taskView = itemViews.get(i);
+            TaskHolder holder = (TaskHolder) mTaskRecyclerView.getChildViewHolder(taskView);
+            if (Objects.equals(holder.getTask(), Optional.of(taskToLaunch))) {
+                mTaskActionController.launchTaskFromView(holder);
+                return;
+            }
+        }
+
+        // Otherwise, just use a basic launch animation.
+        mTaskActionController.launchTask(taskToLaunch);
+    }
+
+    /**
+     * Set whether or not to show the scrim in between the view and the top insets. This only works
+     * if the view is being insetted in the first place.
+     *
+     * The scrim is added to the activity's root view to prevent animations on this view
+     * affecting the scrim. As a result, it is the activity's responsibility to show/hide this
+     * scrim as appropriate.
+     *
+     * @param showStatusBarForegroundScrim true to show the scrim, false to hide
+     */
+    public void setShowStatusBarForegroundScrim(boolean showStatusBarForegroundScrim) {
+        mShowStatusBarForegroundScrim = showStatusBarForegroundScrim;
+        if (mShowStatusBarForegroundScrim != showStatusBarForegroundScrim) {
+            updateStatusBarScrim();
+        }
+    }
+
+    private void updateStatusBarScrim() {
+        boolean shouldShow = mInsets.top != 0 && mShowStatusBarForegroundScrim;
+        mActivity.getDragLayer().setForeground(shouldShow ? mStatusBarForegroundScrim : null);
+    }
+
+    /**
+     * Get the bottom most task view to animate to.
+     *
+     * @return the task view
+     */
+    private @Nullable TaskItemView getBottomTaskView() {
+        int childCount = mTaskRecyclerView.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View view = mTaskRecyclerView.getChildAt(i);
+            if (mTaskRecyclerView.getChildViewHolder(view).getItemViewType() == ITEM_TYPE_TASK) {
+                return (TaskItemView) view;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Whether this view has processed all data changes and is ready to animate from the app to
+     * the overview.
+     *
+     * @return true if ready to animate app to overview, false otherwise
+     */
+    public boolean isReadyForRemoteAnim() {
+        return !mTaskRecyclerView.hasPendingAdapterUpdates();
+    }
+
+    /**
+     * Set a callback for whenever this view is ready to do a remote animation from the app to
+     * overview. See {@link #isReadyForRemoteAnim()}.
+     *
+     * @param callback callback to run when view is ready to animate
+     */
+    public void setOnReadyForRemoteAnimCallback(onReadyForRemoteAnimCallback callback) {
+        mTaskRecyclerView.getViewTreeObserver().addOnGlobalLayoutListener(
+                new ViewTreeObserver.OnGlobalLayoutListener() {
+                    @Override
+                    public void onGlobalLayout() {
+                        if (isReadyForRemoteAnim()) {
+                            callback.onReadyForRemoteAnim();
+                            mTaskRecyclerView.getViewTreeObserver().
+                                    removeOnGlobalLayoutListener(this);
+                        }
+                    }
+                });
+    }
+
+    /**
+     * Clear all tasks and animate out.
+     */
+    private void animateClearAllTasks() {
+        setEnabled(false);
+        ArrayList<TaskItemView> itemViews = getTaskViews();
+
+        AnimatorSet clearAnim = new AnimatorSet();
+        long currentDelay = 0;
+
+        // Animate each item view to the right and fade out.
+        for (int i = 0, size = itemViews.size(); i < size; i++) {
+            TaskItemView itemView = itemViews.get(i);
+            PropertyValuesHolder transXproperty = PropertyValuesHolder.ofFloat(TRANSLATION_X,
+                    0, itemView.getWidth() * ITEM_ANIMATE_OUT_TRANSLATION_X_RATIO);
+            PropertyValuesHolder alphaProperty = PropertyValuesHolder.ofFloat(ALPHA, 1.0f, 0f);
+            ObjectAnimator itemAnim = ObjectAnimator.ofPropertyValuesHolder(itemView,
+                    transXproperty, alphaProperty);
+            itemAnim.setDuration(ITEM_ANIMATE_OUT_DURATION);
+            itemAnim.setStartDelay(currentDelay);
+
+            clearAnim.play(itemAnim);
+            currentDelay += ITEM_ANIMATE_OUT_DELAY_BETWEEN;
+        }
+
+        // Animate view fading and leave recents when faded enough.
+        ValueAnimator contentAlpha = ValueAnimator.ofFloat(1.0f, 0f)
+                .setDuration(CROSSFADE_DURATION);
+        contentAlpha.setStartDelay(CLEAR_ALL_FADE_DELAY);
+        contentAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            private boolean mLeftRecents = false;
+
+            @Override
+            public void onAnimationUpdate(ValueAnimator valueAnimator) {
+                mContentView.setAlpha((float) valueAnimator.getAnimatedValue());
+                // Leave recents while fading out.
+                if ((float) valueAnimator.getAnimatedValue() < .5f && !mLeftRecents) {
+                    mActivityHelper.leaveRecents();
+                    mLeftRecents = true;
+                }
+            }
+        });
+
+        clearAnim.play(contentAlpha);
+        clearAnim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                for (int i = 0, size = itemViews.size(); i < size; i++) {
+                    TaskItemView itemView = itemViews.get(i);
+                    itemView.setTranslationX(0);
+                    itemView.setAlpha(1.0f);
+                }
+                setEnabled(true);
+                mContentView.setVisibility(GONE);
+                mTaskActionController.clearAllTasks();
+            }
+        });
+        clearAnim.start();
+    }
+
+    /**
+     * Get attached task item views ordered by most recent.
+     *
+     * @return array list of attached task item views
+     */
+    private ArrayList<TaskItemView> getTaskViews() {
+        int taskCount = mTaskRecyclerView.getChildCount();
+        ArrayList<TaskItemView> itemViews = new ArrayList<>();
+        for (int i = 0; i < taskCount; i ++) {
+            View child = mTaskRecyclerView.getChildAt(i);
+            if (child instanceof TaskItemView) {
+                itemViews.add((TaskItemView) child);
+            }
+        }
+        return itemViews;
+    }
+
+    /**
+     * Update the content view so that the appropriate view is shown based off the current list
+     * of tasks.
+     */
+    private void updateContentViewVisibility() {
+        int taskListSize = mTaskAdapter.getItemCount() - TASKS_START_POSITION;
+        if (mShowingContentView != mEmptyView && taskListSize == 0) {
+            mShowingContentView = mEmptyView;
+            crossfadeViews(mEmptyView, mContentView);
+        }
+        if (mShowingContentView != mContentView && taskListSize > 0) {
+            mShowingContentView = mContentView;
+            crossfadeViews(mContentView, mEmptyView);
+        }
+    }
+
+    /**
+     * Animate views so that one view fades in while the other fades out.
+     *
+     * @param fadeInView view that should fade in
+     * @param fadeOutView view that should fade out
+     */
+    private void crossfadeViews(View fadeInView, View fadeOutView) {
+        fadeInView.animate().cancel();
+        fadeInView.setVisibility(VISIBLE);
+        fadeInView.setAlpha(0f);
+        fadeInView.animate()
+                .alpha(1f)
+                .setDuration(CROSSFADE_DURATION)
+                .setListener(null);
+
+        fadeOutView.animate().cancel();
+        fadeOutView.animate()
+                .alpha(0f)
+                .setDuration(CROSSFADE_DURATION)
+                .setListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        fadeOutView.setVisibility(GONE);
+                    }
+                });
+    }
+
+    /**
+     * Schedule a one-shot layout animation on the next layout. Separate from
+     * {@link #scheduleLayoutAnimation()} as the animation is {@link Animator} based and acts on the
+     * view properties themselves, allowing more controllable behavior and making it easier to
+     * manage when the animation conflicts with another animation.
+     */
+    private void scheduleFadeInLayoutAnimation() {
+        mTaskRecyclerView.getViewTreeObserver().addOnGlobalLayoutListener(
+                new ViewTreeObserver.OnGlobalLayoutListener() {
+                    @Override
+                    public void onGlobalLayout() {
+                        animateFadeInLayoutAnimation();
+                        mTaskRecyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+                    }
+                });
+    }
+
+    /**
+     * Start animating the layout animation where items fade in.
+     */
+    private void animateFadeInLayoutAnimation() {
+        if (mLayoutAnimation != null) {
+            // If layout animation still in progress, cancel and restart.
+            mLayoutAnimation.cancel();
+        }
+        ArrayList<TaskItemView> views = getTaskViews();
+        int delay = 0;
+        mLayoutAnimation = new AnimatorSet();
+        for (int i = 0, size = views.size(); i < size; i++) {
+            TaskItemView view = views.get(i);
+            view.setAlpha(0.0f);
+            Animator alphaAnim = ObjectAnimator.ofFloat(view, ALPHA, 0.0f, 1.0f);
+            alphaAnim.setDuration(LAYOUT_ITEM_ANIMATE_IN_DURATION).setStartDelay(delay);
+            alphaAnim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    view.setAlpha(1.0f);
+                    mLayingOutViews.remove(view);
+                }
+            });
+            delay += LAYOUT_ITEM_ANIMATE_IN_DELAY_BETWEEN;
+            mLayoutAnimation.play(alphaAnim);
+            mLayingOutViews.add(view);
+        }
+        mLayoutAnimation.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mLayoutAnimation = null;
+            }
+        });
+        mLayoutAnimation.start();
+        mStartedEnterAnimation = true;
+    }
+
+    /**
+     * Play remote app to recents animation when the app is the home activity. We use a simple
+     * cross-fade here. Note this is only used if the home activity is a separate app than the
+     * recents activity.
+     *
+     * @param anim animator set
+     * @param homeTarget the home surface thats closing
+     * @param recentsTarget the surface containing recents
+     */
+    public void playRemoteHomeToRecentsAnimation(@NonNull AnimatorSet anim,
+            @NonNull RemoteAnimationTargetCompat homeTarget,
+            @NonNull RemoteAnimationTargetCompat recentsTarget) {
+        SyncRtSurfaceTransactionApplierCompat surfaceApplier =
+                new SyncRtSurfaceTransactionApplierCompat(this);
+
+        SurfaceParams[] params = new SurfaceParams[2];
+        int boostedMode = MODE_CLOSING;
+
+        ValueAnimator remoteHomeAnim = ValueAnimator.ofFloat(0, 1);
+        remoteHomeAnim.setDuration(REMOTE_APP_TO_OVERVIEW_DURATION);
+
+        remoteHomeAnim.addUpdateListener(valueAnimator -> {
+            float val = (float) valueAnimator.getAnimatedValue();
+            float alpha;
+            RemoteAnimationTargetCompat visibleTarget;
+            RemoteAnimationTargetCompat invisibleTarget;
+            if (val < .5f) {
+                visibleTarget = homeTarget;
+                invisibleTarget = recentsTarget;
+                alpha = 1 - (val * 2);
+            } else {
+                visibleTarget = recentsTarget;
+                invisibleTarget = homeTarget;
+                alpha = (val - .5f) * 2;
+            }
+            params[0] = new SurfaceParams(visibleTarget.leash, alpha, null /* matrix */,
+                    null /* windowCrop */, getLayer(visibleTarget, boostedMode),
+                    0 /* cornerRadius */);
+            params[1] = new SurfaceParams(invisibleTarget.leash, 0.0f, null /* matrix */,
+                    null /* windowCrop */, getLayer(invisibleTarget, boostedMode),
+                    0 /* cornerRadius */);
+            surfaceApplier.scheduleApply(params);
+        });
+        anim.play(remoteHomeAnim);
+        animateFadeInLayoutAnimation();
+    }
+
+    /**
+     * Play remote animation from app to recents. This should scale the currently closing app down
+     * to the recents thumbnail.
+     *
+     * @param anim animator set
+     * @param appTarget the app surface thats closing
+     * @param recentsTarget the surface containing recents
+     */
+    public void playRemoteAppToRecentsAnimation(@NonNull AnimatorSet anim,
+            @NonNull RemoteAnimationTargetCompat appTarget,
+            @NonNull RemoteAnimationTargetCompat recentsTarget) {
+        TaskItemView bottomView = getBottomTaskView();
+        if (bottomView == null) {
+            // This can be null if there were previously 0 tasks and the recycler view has not had
+            // enough time to take in the data change, bind a new view, and lay out the new view.
+            // TODO: Have a fallback to animate to
+            anim.play(ValueAnimator.ofInt(0, 1).setDuration(REMOTE_APP_TO_OVERVIEW_DURATION));
+            return;
+        }
+        final Matrix appMatrix = new Matrix();
+        playRemoteTransYAnim(anim, appMatrix);
+        playRemoteAppScaleDownAnim(anim, appMatrix, appTarget, recentsTarget,
+                bottomView.getThumbnailView());
+        playRemoteTaskListFadeIn(anim, bottomView);
+        mStartedEnterAnimation = true;
+    }
+
+    /**
+     * Play translation Y animation for the remote app to recents animation. Animates over all task
+     * views as well as the closing app, easing them into their final vertical positions.
+     *
+     * @param anim animator set to play on
+     * @param appMatrix transformation matrix for the closing app surface
+     */
+    private void playRemoteTransYAnim(@NonNull AnimatorSet anim, @NonNull Matrix appMatrix) {
+        final ArrayList<TaskItemView> views = getTaskViews();
+
+        // Start Y translation from about halfway through the tasks list to the bottom thumbnail.
+        float taskHeight = getResources().getDimension(R.dimen.task_item_height);
+        float totalTransY = -(MAX_TASKS_TO_DISPLAY / 2.0f - 1) * taskHeight;
+        for (int i = 0, size = views.size(); i < size; i++) {
+            views.get(i).setTranslationY(totalTransY);
+        }
+
+        ValueAnimator transYAnim = ValueAnimator.ofFloat(totalTransY, 0);
+        transYAnim.setDuration(REMOTE_TO_RECENTS_VERTICAL_EASE_IN_DURATION);
+        transYAnim.setInterpolator(FAST_OUT_SLOW_IN_2);
+        transYAnim.addUpdateListener(valueAnimator -> {
+            float transY = (float) valueAnimator.getAnimatedValue();
+            for (int i = 0, size = views.size(); i < size; i++) {
+                views.get(i).setTranslationY(transY);
+            }
+            appMatrix.postTranslate(0, transY - totalTransY);
+        });
+        transYAnim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                for (int i = 0, size = views.size(); i < size; i++) {
+                    views.get(i).setTranslationY(0);
+                }
+            }
+        });
+        anim.play(transYAnim);
+    }
+
+    /**
+     * Play the scale down animation for the remote app to recents animation where the app surface
+     * scales down to where the thumbnail is.
+     *
+     * @param anim animator set to play on
+     * @param appMatrix transformation matrix for the app surface
+     * @param appTarget closing app target
+     * @param recentsTarget opening recents target
+     * @param thumbnailView thumbnail view to animate to
+     */
+    private void playRemoteAppScaleDownAnim(@NonNull AnimatorSet anim, @NonNull Matrix appMatrix,
+            @NonNull RemoteAnimationTargetCompat appTarget,
+            @NonNull RemoteAnimationTargetCompat recentsTarget,
+            @NonNull View thumbnailView) {
+        // Identify where the entering remote app should animate to.
+        Rect endRect = new Rect();
+        thumbnailView.getGlobalVisibleRect(endRect);
+        Rect appBounds = appTarget.sourceContainerBounds;
+        RectF currentAppRect = new RectF();
+
+        SyncRtSurfaceTransactionApplierCompat surfaceApplier =
+                new SyncRtSurfaceTransactionApplierCompat(this);
+
+        // Keep recents visible throughout the animation.
+        SurfaceParams[] params = new SurfaceParams[2];
+        // Closing app should stay on top.
+        int boostedMode = MODE_CLOSING;
+        params[0] = new SurfaceParams(recentsTarget.leash, 1f, null /* matrix */,
+                null /* windowCrop */, getLayer(recentsTarget, boostedMode), 0 /* cornerRadius */);
+
+        ValueAnimator remoteAppAnim = ValueAnimator.ofInt(0, 1);
+        remoteAppAnim.setDuration(REMOTE_TO_RECENTS_VERTICAL_EASE_IN_DURATION);
+        remoteAppAnim.addUpdateListener(new MultiValueUpdateListener() {
+            private final FloatProp mScaleX;
+            private final FloatProp mScaleY;
+            private final FloatProp mTranslationX;
+            private final FloatProp mTranslationY;
+            private final FloatProp mAlpha;
+
+            {
+                // Scale down and move to view location.
+                float endScaleX = ((float) endRect.width()) / appBounds.width();
+                mScaleX = new FloatProp(1f, endScaleX, 0, REMOTE_TO_RECENTS_APP_SCALE_DOWN_DURATION,
+                        FAST_OUT_SLOW_IN_1);
+                float endScaleY = ((float) endRect.height()) / appBounds.height();
+                mScaleY = new FloatProp(1f, endScaleY, 0, REMOTE_TO_RECENTS_APP_SCALE_DOWN_DURATION,
+                        FAST_OUT_SLOW_IN_1);
+                float endTranslationX = endRect.left -
+                        (appBounds.width() - thumbnailView.getWidth()) / 2.0f;
+                mTranslationX = new FloatProp(0, endTranslationX, 0,
+                        REMOTE_TO_RECENTS_APP_SCALE_DOWN_DURATION, FAST_OUT_SLOW_IN_1);
+                float endTranslationY = endRect.top -
+                        (appBounds.height() - thumbnailView.getHeight()) / 2.0f;
+                mTranslationY = new FloatProp(0, endTranslationY, 0,
+                        REMOTE_TO_RECENTS_APP_SCALE_DOWN_DURATION, FAST_OUT_SLOW_IN_2);
+                mAlpha = new FloatProp(1.0f, 0, 0, REMOTE_TO_RECENTS_APP_SCALE_DOWN_DURATION,
+                        ACCEL_2);
+            }
+
+            @Override
+            public void onUpdate(float percent) {
+                Matrix m = new Matrix();
+                m.preScale(mScaleX.value, mScaleY.value,
+                        appBounds.width() / 2.0f, appBounds.height() / 2.0f);
+                m.postTranslate(mTranslationX.value, mTranslationY.value);
+                appMatrix.preConcat(m);
+                params[1] = new SurfaceParams(appTarget.leash, mAlpha.value, appMatrix,
+                        null /* windowCrop */, getLayer(appTarget, boostedMode),
+                        0 /* cornerRadius */);
+                surfaceApplier.scheduleApply(params);
+
+                m.mapRect(currentAppRect, new RectF(appBounds));
+                setViewToRect(thumbnailView, new RectF(endRect), currentAppRect);
+                appMatrix.reset();
+            }
+        });
+        remoteAppAnim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                thumbnailView.setTranslationY(0);
+                thumbnailView.setTranslationX(0);
+                thumbnailView.setScaleX(1);
+                thumbnailView.setScaleY(1);
+            }
+        });
+        anim.play(remoteAppAnim);
+    }
+
+    /**
+     * Play task list fade in animation as part of remote app to recents animation. This animation
+     * ensures that the task views in the recents list fade in from bottom to top.
+     *
+     * @param anim animator set to play on
+     * @param appTaskView the task view associated with the remote app closing
+     */
+    private void playRemoteTaskListFadeIn(@NonNull AnimatorSet anim,
+            @NonNull TaskItemView appTaskView) {
+        long delay = REMOTE_TO_RECENTS_ITEM_FADE_START_DELAY;
+        int childCount = mTaskRecyclerView.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            ValueAnimator fadeAnim = ValueAnimator.ofFloat(0, 1.0f);
+            fadeAnim.setDuration(REMOTE_TO_RECENTS_ITEM_FADE_DURATION).setInterpolator(OUT_SLOW_IN);
+            fadeAnim.setStartDelay(delay);
+            View view = mTaskRecyclerView.getChildAt(i);
+            if (Objects.equals(view, appTaskView)) {
+                // Only animate icon and text for the view with snapshot animating in
+                final View icon = appTaskView.getIconView();
+                final View label = appTaskView.getLabelView();
+
+                icon.setAlpha(0.0f);
+                label.setAlpha(0.0f);
+
+                fadeAnim.addUpdateListener(alphaVal -> {
+                    float val = alphaVal.getAnimatedFraction();
+
+                    icon.setAlpha(val);
+                    label.setAlpha(val);
+                });
+                fadeAnim.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        icon.setAlpha(1.0f);
+                        label.setAlpha(1.0f);
+                    }
+                });
+            } else {
+                // Otherwise, fade in the entire view.
+                view.setAlpha(0.0f);
+                fadeAnim.addUpdateListener(alphaVal -> {
+                    float val = alphaVal.getAnimatedFraction();
+                    view.setAlpha(val);
+                });
+                fadeAnim.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        view.setAlpha(1.0f);
+                    }
+                });
+            }
+            anim.play(fadeAnim);
+
+            int itemType = mTaskRecyclerView.getChildViewHolder(view).getItemViewType();
+            if (itemType == ITEM_TYPE_CLEAR_ALL) {
+                // Don't add delay. Clear all should animate at same time as next view.
+                continue;
+            }
+            delay += REMOTE_TO_RECENTS_ITEM_FADE_BETWEEN_DELAY;
+        }
+    }
+
+    /**
+     * Set view properties so that the view fits to the target rect.
+     *
+     * @param view view to set
+     * @param origRect original rect that view was located
+     * @param targetRect rect to set to
+     */
+    private void setViewToRect(View view, RectF origRect, RectF targetRect) {
+        float dX = targetRect.left - origRect.left;
+        float dY = targetRect.top - origRect.top;
+        view.setTranslationX(dX);
+        view.setTranslationY(dY);
+
+        float scaleX = targetRect.width() / origRect.width();
+        float scaleY = targetRect.height() / origRect.height();
+        view.setPivotX(0);
+        view.setPivotY(0);
+        view.setScaleX(scaleX);
+        view.setScaleY(scaleY);
+    }
+
+    @Override
+    public void setInsets(Rect insets) {
+        mInsets = insets;
+        mTaskRecyclerView.setPadding(insets.left, insets.top, insets.right, insets.bottom);
+        mTaskRecyclerView.invalidateItemDecorations();
+        if (mInsets.top != 0) {
+            updateStatusBarScrim();
+        }
+    }
+
+    /**
+     * Callback for when this view is ready for a remote animation from app to overview.
+     */
+    public interface onReadyForRemoteAnimCallback {
+
+        void onReadyForRemoteAnim();
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
new file mode 100644
index 0000000..f184ad0
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.views;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.FloatProperty;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.R;
+import com.android.quickstep.ThumbnailDrawable;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+
+/**
+ * View representing an individual task item with the icon + thumbnail adjacent to the task label.
+ */
+public final class TaskItemView extends LinearLayout {
+
+    private static final String EMPTY_LABEL = "";
+    private static final String DEFAULT_LABEL = "...";
+    private final Drawable mDefaultIcon;
+    private final Drawable mDefaultThumbnail;
+    private final TaskLayerDrawable mIconDrawable;
+    private final TaskLayerDrawable mThumbnailDrawable;
+    private View mTaskIconThumbnailView;
+    private TextView mLabelView;
+    private ImageView mIconView;
+    private ImageView mThumbnailView;
+    private float mContentTransitionProgress;
+    private int mDisplayedOrientation;
+
+    /**
+     * Property representing the content transition progress of the view. 1.0f represents that the
+     * currently bound icon, thumbnail, and label are fully animated in and visible.
+     */
+    public static FloatProperty CONTENT_TRANSITION_PROGRESS =
+            new FloatProperty<TaskItemView>("taskContentTransitionProgress") {
+                @Override
+                public void setValue(TaskItemView view, float progress) {
+                    view.setContentTransitionProgress(progress);
+                }
+
+                @Override
+                public Float get(TaskItemView view) {
+                    return view.mContentTransitionProgress;
+                }
+            };
+
+    public TaskItemView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        Resources res = context.getResources();
+        mDefaultIcon = res.getDrawable(android.R.drawable.sym_def_app_icon, context.getTheme());
+        mDefaultThumbnail = res.getDrawable(R.drawable.default_thumbnail, context.getTheme());
+        mIconDrawable = new TaskLayerDrawable(context);
+        mThumbnailDrawable = new TaskLayerDrawable(context);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mLabelView = findViewById(R.id.task_label);
+        mTaskIconThumbnailView = findViewById(R.id.task_icon_and_thumbnail);
+        mThumbnailView = findViewById(R.id.task_thumbnail);
+        mIconView = findViewById(R.id.task_icon);
+
+        mThumbnailView.setImageDrawable(mThumbnailDrawable);
+        mIconView.setImageDrawable(mIconDrawable);
+
+        resetToEmptyUi();
+        CONTENT_TRANSITION_PROGRESS.setValue(this, 1.0f);
+    }
+
+    /**
+     * Resets task item view to empty, loading UI.
+     */
+    public void resetToEmptyUi() {
+        mIconDrawable.resetDrawable();
+        mThumbnailDrawable.resetDrawable();
+        setLabel(EMPTY_LABEL);
+    }
+
+    /**
+     * Set the label for the task item. Sets to a default label if null.
+     *
+     * @param label task label
+     */
+    public void setLabel(@Nullable String label) {
+        mLabelView.setText(getSafeLabel(label));
+        // TODO: Animation for label
+    }
+
+    /**
+     * Set the icon for the task item. Sets to a default icon if null.
+     *
+     * @param icon task icon
+     */
+    public void setIcon(@Nullable Drawable icon) {
+        // TODO: Scale the icon up based off the padding on the side
+        // The icon proper is actually smaller than the drawable and has "padding" on the side for
+        // the purpose of drawing the shadow, allowing the icon to pop up, so we need to scale the
+        // view if we want the icon to be flush with the bottom of the thumbnail.
+        mIconDrawable.setCurrentDrawable(getSafeIcon(icon));
+    }
+
+    /**
+     * Set the task thumbnail for the task. Sets to a default thumbnail if null.
+     *
+     * @param thumbnailData task thumbnail data for the task
+     */
+    public void setThumbnail(@Nullable ThumbnailData thumbnailData) {
+        mThumbnailDrawable.setCurrentDrawable(getSafeThumbnail(thumbnailData));
+    }
+
+    public View getThumbnailView() {
+        return mThumbnailView;
+    }
+
+    public View getIconView() {
+        return mIconView;
+    }
+
+    public View getLabelView() {
+        return mLabelView;
+    }
+
+    /**
+     * Start a new animation from the current task content to the specified new content. The caller
+     * is responsible for the actual animation control via the property
+     * {@link #CONTENT_TRANSITION_PROGRESS}.
+     *
+     * @param endIcon the icon to animate to
+     * @param endThumbnail the thumbnail to animate to
+     * @param endLabel the label to animate to
+     */
+    public void startContentAnimation(@Nullable Drawable endIcon,
+            @Nullable ThumbnailData endThumbnail, @Nullable String endLabel) {
+        mIconDrawable.startNewTransition(getSafeIcon(endIcon));
+        mThumbnailDrawable.startNewTransition(getSafeThumbnail(endThumbnail));
+        // TODO: Animation for label
+
+        setContentTransitionProgress(0.0f);
+    }
+
+    private void setContentTransitionProgress(float progress) {
+        mContentTransitionProgress = progress;
+        mIconDrawable.setTransitionProgress(progress);
+        mThumbnailDrawable.setTransitionProgress(progress);
+        // TODO: Animation for label
+    }
+
+    private @NonNull Drawable getSafeIcon(@Nullable Drawable icon) {
+        return (icon != null) ? icon : mDefaultIcon;
+    }
+
+    private @NonNull Drawable getSafeThumbnail(@Nullable ThumbnailData thumbnailData) {
+        if (thumbnailData == null || thumbnailData.thumbnail == null) {
+            return mDefaultThumbnail;
+        }
+        int orientation = getResources().getConfiguration().orientation;
+        return new ThumbnailDrawable(getResources(), thumbnailData,
+                orientation /* requestedOrientation */);
+    }
+
+    private @NonNull String getSafeLabel(@Nullable String label) {
+        return (label != null) ? label : DEFAULT_LABEL;
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        onOrientationChanged(getResources().getConfiguration().orientation);
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        onOrientationChanged(newConfig.orientation);
+    }
+
+    private void onOrientationChanged(int newOrientation) {
+        if (mDisplayedOrientation == newOrientation) {
+            return;
+        }
+        mDisplayedOrientation = newOrientation;
+        int layerCount = mThumbnailDrawable.getNumberOfLayers();
+        for (int i = 0; i < layerCount; i++) {
+            Drawable drawable = mThumbnailDrawable.getDrawable(i);
+            if (drawable instanceof ThumbnailDrawable) {
+                ((ThumbnailDrawable) drawable).setRequestedOrientation(newOrientation);
+            }
+        }
+        mTaskIconThumbnailView.forceLayout();
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/views/TaskLayerDrawable.java b/go/quickstep/src/com/android/quickstep/views/TaskLayerDrawable.java
new file mode 100644
index 0000000..98b66b9
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/views/TaskLayerDrawable.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.views;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.R;
+
+/**
+ * A layer drawable for task content that transitions between two drawables by crossfading. Similar
+ * to {@link android.graphics.drawable.TransitionDrawable} but allows callers to control transition
+ * progress and provides a default, empty drawable.
+ */
+public final class TaskLayerDrawable extends LayerDrawable {
+    private final Drawable mEmptyDrawable;
+    private float mProgress;
+
+    public TaskLayerDrawable(Context context) {
+        super(new Drawable[0]);
+
+        // Use empty drawable for both layers initially.
+        mEmptyDrawable = context.getResources().getDrawable(
+                R.drawable.empty_content_box, context.getTheme());
+        addLayer(mEmptyDrawable);
+        addLayer(mEmptyDrawable);
+        setTransitionProgress(1.0f);
+    }
+
+    /**
+     * Immediately set the front-most drawable layer.
+     *
+     * @param drawable drawable to set
+     */
+    public void setCurrentDrawable(@NonNull Drawable drawable) {
+        setDrawable(0, drawable);
+        applyTransitionProgress(mProgress);
+    }
+
+    /**
+     * Immediately reset the drawable to showing the empty drawable.
+     */
+    public void resetDrawable() {
+        setCurrentDrawable(mEmptyDrawable);
+    }
+
+    /**
+     * Prepare to start animating the transition by pushing the current drawable to the back and
+     * setting a new drawable to the front layer and making it invisible.
+     *
+     * @param endDrawable drawable to animate to
+     */
+    public void startNewTransition(@NonNull Drawable endDrawable) {
+        Drawable oldDrawable = getDrawable(0);
+        setDrawable(1, oldDrawable);
+        setDrawable(0, endDrawable);
+        setTransitionProgress(0.0f);
+    }
+
+    /**
+     * Set the progress of the transition animation to crossfade the two drawables.
+     *
+     * @param progress current transition progress between 0 (front view invisible) and 1
+     *                 (front view visible)
+     */
+    public void setTransitionProgress(float progress) {
+        if (progress > 1 || progress < 0) {
+            throw new IllegalArgumentException("Transition progress should be between 0 and 1");
+        }
+        mProgress = progress;
+        applyTransitionProgress(progress);
+    }
+
+    private void applyTransitionProgress(float progress) {
+        int drawableAlpha = (int) (progress * 255);
+        getDrawable(0).setAlpha(drawableAlpha);
+        if (getDrawable(0) != getDrawable(1)) {
+            // Only do this if it's a different drawable so that it fades out.
+            // Otherwise, we'd just be overwriting the front drawable's alpha.
+            getDrawable(1).setAlpha(255 - drawableAlpha);
+        }
+        invalidateSelf();
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/views/TaskThumbnailIconView.java b/go/quickstep/src/com/android/quickstep/views/TaskThumbnailIconView.java
new file mode 100644
index 0000000..eaefa21
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/views/TaskThumbnailIconView.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.views;
+
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.view.View.MeasureSpec.makeMeasureSpec;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.launcher3.R;
+
+/**
+ * Square view that holds thumbnail and icon and shrinks them appropriately so that both fit nicely
+ * within the view. Side length is determined by height.
+ */
+final class TaskThumbnailIconView extends ViewGroup {
+    private final Rect mTmpFrameRect = new Rect();
+    private final Rect mTmpChildRect = new Rect();
+    private View mThumbnailView;
+    private View mIconView;
+    private static final float SUBITEM_FRAME_RATIO = .6f;
+
+    public TaskThumbnailIconView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mThumbnailView = findViewById(R.id.task_thumbnail);
+        mIconView = findViewById(R.id.task_icon);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
+        int width = height;
+        setMeasuredDimension(width, height);
+
+        int subItemSize = (int) (SUBITEM_FRAME_RATIO * height);
+        if (mThumbnailView.getVisibility() != GONE) {
+            boolean isPortrait =
+                    (getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT);
+            int thumbnailHeightSpec =
+                    makeMeasureSpec(isPortrait ? height : subItemSize, MeasureSpec.EXACTLY);
+            int thumbnailWidthSpec =
+                    makeMeasureSpec(isPortrait ? subItemSize : width, MeasureSpec.EXACTLY);
+            measureChild(mThumbnailView, thumbnailWidthSpec, thumbnailHeightSpec);
+        }
+        if (mIconView.getVisibility() != GONE) {
+            int iconHeightSpec = makeMeasureSpec(subItemSize, MeasureSpec.EXACTLY);
+            int iconWidthSpec = makeMeasureSpec(subItemSize, MeasureSpec.EXACTLY);
+            measureChild(mIconView, iconWidthSpec, iconHeightSpec);
+        }
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        mTmpFrameRect.left = getPaddingLeft();
+        mTmpFrameRect.right = right - left - getPaddingRight();
+        mTmpFrameRect.top = getPaddingTop();
+        mTmpFrameRect.bottom = bottom - top - getPaddingBottom();
+
+        // Layout the thumbnail to the top-start corner of the view
+        if (mThumbnailView.getVisibility() != GONE) {
+            final int width = mThumbnailView.getMeasuredWidth();
+            final int height = mThumbnailView.getMeasuredHeight();
+
+            final int thumbnailGravity = Gravity.TOP | Gravity.START;
+            Gravity.apply(thumbnailGravity, width, height, mTmpFrameRect, mTmpChildRect);
+
+            mThumbnailView.layout(mTmpChildRect.left, mTmpChildRect.top,
+                    mTmpChildRect.right, mTmpChildRect.bottom);
+        }
+
+        // Layout the icon to the bottom-end corner of the view
+        if (mIconView.getVisibility() != GONE) {
+            final int width = mIconView.getMeasuredWidth();
+            final int height = mIconView.getMeasuredHeight();
+
+            int thumbnailGravity = Gravity.BOTTOM | Gravity.END;
+            Gravity.apply(thumbnailGravity, width, height, mTmpFrameRect, mTmpChildRect);
+
+            mIconView.layout(mTmpChildRect.left, mTmpChildRect.top,
+                    mTmpChildRect.right, mTmpChildRect.bottom);
+        }
+    }
+}
diff --git a/go/res/drawable/ic_widget.xml b/go/res/drawable/ic_widget.xml
deleted file mode 100644
index 5336876..0000000
--- a/go/res/drawable/ic_widget.xml
+++ /dev/null
@@ -1,25 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="48dp"
-        android:height="48dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M3.9,18.35c2.5-2.49,5.78-3.64,10.14-3.64v3.05c0,0.47,0.57,0.71,0.9,0.37l5.74-5.74c0.41-0.41,0.41-1.08,0-1.49l-5.74-5.74
-        c-0.33-0.33-0.9-0.1-0.9,0.37v2.95c-6.32,0.9-9.56,4.9-11.02,9.34C2.86,18.34,3.51,18.74,3.9,18.35z"/>
-</vector>
diff --git a/go/res/layout/widget_cell_content.xml b/go/res/layout/widget_cell_content.xml
deleted file mode 100644
index 49506d9..0000000
--- a/go/res/layout/widget_cell_content.xml
+++ /dev/null
@@ -1,66 +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.
--->
-<merge xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content">
-
-    <LinearLayout
-        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">
-
-        <!-- 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:fontFamily="sans-serif-condensed"
-            android:gravity="center"
-            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:visibility="gone"
-            android:textColor="?android:attr/textColorSecondary"
-            android:textSize="14sp"
-            android:fontFamily="sans-serif-condensed"
-            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"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="1" />
-</merge>
\ No newline at end of file
diff --git a/go/res/values-af/strings.xml b/go/res/values-af/strings.xml
deleted file mode 100644
index 10ac473..0000000
--- a/go/res/values-af/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Raak en hou om \'n kortpad op te tel."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Dubbeltik en hou om \'n kortpad op te tel of gebruik gepasmaakte handelinge."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Kortpaaie"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g>-kortpaaie"</string>
-</resources>
diff --git a/go/res/values-am/strings.xml b/go/res/values-am/strings.xml
deleted file mode 100644
index b3b253f..0000000
--- a/go/res/values-am/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"አንድ አቋራጭ ለመውሰድ ነክተው ይያዙ"</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"አንድ አቋራጭ ለመውሰድ ወይም ብጁ እርምጃዎችን ለመጠቀም ሁለቴ መታ አድርገው ይያዙ።"</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"አቋራጮች"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> አቋራጮች"</string>
-</resources>
diff --git a/go/res/values-ar/strings.xml b/go/res/values-ar/strings.xml
deleted file mode 100644
index 9888d0f..0000000
--- a/go/res/values-ar/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"المس مع الاستمرار لاختيار اختصار."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"يمكنك النقر مرّتين مع الاستمرار لاختيار اختصار أو استخدام الإجراءات المخصصة."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"الاختصارات"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"اختصارات <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-as/strings.xml b/go/res/values-as/strings.xml
deleted file mode 100644
index 6b5807f..0000000
--- a/go/res/values-as/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"কোনো শ্বৰ্টকাট বাছনি কৰিবলৈ স্পৰ্শ কৰি ৰাখক।"</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"কোনো শ্বৰ্টকাট বাছনি কৰিবলৈ দুবাৰ টিপি ৰাখক বা নিজৰ উপযোগিতা অনুসৰি বনোৱা কাৰ্যসমূহ ব্যৱহাৰ কৰক।"</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"শ্বৰ্টকাটসমূহ"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> শ্বৰ্টকাটসমূহ"</string>
-</resources>
diff --git a/go/res/values-az-rAZ/strings.xml b/go/res/values-az-rAZ/strings.xml
deleted file mode 100644
index c4b8cb7..0000000
--- a/go/res/values-az-rAZ/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Qısayolu seçmək üçün toxunub saxlayın."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Qısayolu seçmək üçün iki dəfə basıb saxlayın və ya fərdi əməliyyatlardan istifadə edin."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Qısayollar"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> qısayolları"</string>
-</resources>
diff --git a/go/res/values-az/strings.xml b/go/res/values-az/strings.xml
deleted file mode 100644
index c4b8cb7..0000000
--- a/go/res/values-az/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Qısayolu seçmək üçün toxunub saxlayın."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Qısayolu seçmək üçün iki dəfə basıb saxlayın və ya fərdi əməliyyatlardan istifadə edin."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Qısayollar"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> qısayolları"</string>
-</resources>
diff --git a/go/res/values-b+sr+Latn/strings.xml b/go/res/values-b+sr+Latn/strings.xml
deleted file mode 100644
index 0da5699..0000000
--- a/go/res/values-b+sr+Latn/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Dodirnite i zadržite da biste izabrali prečicu."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Dvaput dodirnite i zadržite da biste izabrali prečicu ili koristite prilagođene radnje."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Prečice"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Prečice za <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-be-rBY/strings.xml b/go/res/values-be-rBY/strings.xml
deleted file mode 100644
index 4189e35..0000000
--- a/go/res/values-be-rBY/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Дакраніцеся і ўтрымлiвайце ярлык, каб дадаць яго."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Дакраніцеся двойчы і ўтрымлівайце, каб выбраць ярлык або выкарыстоўваць спецыяльныя дзеянні."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Ярлыкі"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Ярлыкі <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-be/strings.xml b/go/res/values-be/strings.xml
deleted file mode 100644
index 4189e35..0000000
--- a/go/res/values-be/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Дакраніцеся і ўтрымлiвайце ярлык, каб дадаць яго."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Дакраніцеся двойчы і ўтрымлівайце, каб выбраць ярлык або выкарыстоўваць спецыяльныя дзеянні."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Ярлыкі"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Ярлыкі <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-bg/strings.xml b/go/res/values-bg/strings.xml
deleted file mode 100644
index 1b85992..0000000
--- a/go/res/values-bg/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Докоснете и задръжте за избор на пряк път."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Докоснете двукратно и задръжте за избор на пряк път или използвайте персонализирани действия."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Преки пътища"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Преки пътища за <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-bn-rBD/strings.xml b/go/res/values-bn-rBD/strings.xml
deleted file mode 100644
index c56c925..0000000
--- a/go/res/values-bn-rBD/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"কোনও শর্টকাট বেছে নিতে টাচ করে ধরে রাখুন।"</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"কোনও শর্টকাট বেছে নিতে ডাবল ট্যাপ করে ধরে রাখুন অথবা কাস্টম ক্রিয়াগুলি ব্যবহার করুন।"</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"শর্টকাট"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> এর শর্টকাট"</string>
-</resources>
diff --git a/go/res/values-bn/strings.xml b/go/res/values-bn/strings.xml
deleted file mode 100644
index c56c925..0000000
--- a/go/res/values-bn/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"কোনও শর্টকাট বেছে নিতে টাচ করে ধরে রাখুন।"</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"কোনও শর্টকাট বেছে নিতে ডাবল ট্যাপ করে ধরে রাখুন অথবা কাস্টম ক্রিয়াগুলি ব্যবহার করুন।"</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"শর্টকাট"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> এর শর্টকাট"</string>
-</resources>
diff --git a/go/res/values-bs-rBA/strings.xml b/go/res/values-bs-rBA/strings.xml
deleted file mode 100644
index 3141b9d..0000000
--- a/go/res/values-bs-rBA/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Dodirnite i držite da uzmete prečicu."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Dodirnite dvaput i držite da uzmete prečicu ili koristite prilagođene akcije."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Prečice"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Prečice aplikacije <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-bs/strings.xml b/go/res/values-bs/strings.xml
deleted file mode 100644
index 3141b9d..0000000
--- a/go/res/values-bs/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Dodirnite i držite da uzmete prečicu."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Dodirnite dvaput i držite da uzmete prečicu ili koristite prilagođene akcije."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Prečice"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Prečice aplikacije <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-ca/strings.xml b/go/res/values-ca/strings.xml
deleted file mode 100644
index 3b5c3f7..0000000
--- a/go/res/values-ca/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Mantén premuda una drecera per seleccionar-la."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Fes doble toc i mantén premut per seleccionar una drecera o per utilitzar accions personalitzades."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Dreceres"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Dreceres de l\'aplicació <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-cs/strings.xml b/go/res/values-cs/strings.xml
deleted file mode 100644
index e4018f2..0000000
--- a/go/res/values-cs/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Zkratku vyberete podržením."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Dvojitým klepnutím a podržením vyberte zkratku, případně použijte vlastní akce."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Zkratky"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Zkratky aplikace <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-da/strings.xml b/go/res/values-da/strings.xml
deleted file mode 100644
index 821d36a..0000000
--- a/go/res/values-da/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Hold en genvej nede for at samle den op."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Tryk to gange, og hold en genvej nede for at samle den op og bruge tilpassede handlinger."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Genveje"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g>-genveje"</string>
-</resources>
diff --git a/go/res/values-de/strings.xml b/go/res/values-de/strings.xml
deleted file mode 100644
index 43a1b3a..0000000
--- a/go/res/values-de/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Doppeltippen und halten, um eine Verknüpfung auszuwählen."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Doppeltippen und halten, um eine Verknüpfung auszuwählen oder benutzerdefinierte Aktionen zu nutzen."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Verknüpfungen"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g>-Verknüpfungen"</string>
-</resources>
diff --git a/go/res/values-el/strings.xml b/go/res/values-el/strings.xml
deleted file mode 100644
index ae59907..0000000
--- a/go/res/values-el/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Αγγίξτε παρατεταμένα για να σηκώσετε μια συντόμευση."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Πατήσετε δύο φορές παρατεταμένα για να σηκώσετε μια συντόμευση ή για να χρησιμοποιήσετε προσαρμοσμένες ενέργειες."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Συντομεύσεις"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Συντομεύσεις <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-en-rAU/strings.xml b/go/res/values-en-rAU/strings.xml
deleted file mode 100644
index 2ee2c26..0000000
--- a/go/res/values-en-rAU/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Touch &amp; hold to pick up a shortcut."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Double-tap &amp; hold to pick up a shortcut or use custom actions."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Shortcuts"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> shortcuts"</string>
-</resources>
diff --git a/go/res/values-en-rGB/strings.xml b/go/res/values-en-rGB/strings.xml
deleted file mode 100644
index 2ee2c26..0000000
--- a/go/res/values-en-rGB/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Touch &amp; hold to pick up a shortcut."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Double-tap &amp; hold to pick up a shortcut or use custom actions."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Shortcuts"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> shortcuts"</string>
-</resources>
diff --git a/go/res/values-en-rIN/strings.xml b/go/res/values-en-rIN/strings.xml
deleted file mode 100644
index 2ee2c26..0000000
--- a/go/res/values-en-rIN/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Touch &amp; hold to pick up a shortcut."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Double-tap &amp; hold to pick up a shortcut or use custom actions."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Shortcuts"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> shortcuts"</string>
-</resources>
diff --git a/go/res/values-es-rUS/strings.xml b/go/res/values-es-rUS/strings.xml
deleted file mode 100644
index 5212c03..0000000
--- a/go/res/values-es-rUS/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Mantén presionado para elegir un acceso directo."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Presiona dos veces y mantén presionado para elegir un acceso directo o usar acciones personalizadas."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Accesos directos"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Accesos directos de <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-es/strings.xml b/go/res/values-es/strings.xml
deleted file mode 100644
index 3ae4588..0000000
--- a/go/res/values-es/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Mantén pulsado el acceso directo que quieras."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Toca dos veces y mantén pulsado el acceso directo o utiliza acciones personalizadas."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Accesos directos"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Accesos directos de <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-et-rEE/strings.xml b/go/res/values-et-rEE/strings.xml
deleted file mode 100644
index 2513e65..0000000
--- a/go/res/values-et-rEE/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Otsetee valimiseks puudutage seda pikalt."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Topeltpuudutage ja hoidke otsetee valimiseks või kohandatud toimingute kasutamiseks."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Otseteed"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Rakenduse <xliff:g id="NAME">%1$s</xliff:g> otseteed"</string>
-</resources>
diff --git a/go/res/values-et/strings.xml b/go/res/values-et/strings.xml
deleted file mode 100644
index 2513e65..0000000
--- a/go/res/values-et/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Otsetee valimiseks puudutage seda pikalt."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Topeltpuudutage ja hoidke otsetee valimiseks või kohandatud toimingute kasutamiseks."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Otseteed"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Rakenduse <xliff:g id="NAME">%1$s</xliff:g> otseteed"</string>
-</resources>
diff --git a/go/res/values-eu-rES/strings.xml b/go/res/values-eu-rES/strings.xml
deleted file mode 100644
index 9949ef0..0000000
--- a/go/res/values-eu-rES/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Eduki sakatuta lasterbide bat aukeratzeko."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Sakatu birritan eta eduki sakatuta lasterbide bat aukeratzeko edo ekintza pertsonalizatuak erabiltzeko."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Lasterbideak"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> aplikazioaren lasterbidea"</string>
-</resources>
diff --git a/go/res/values-eu/strings.xml b/go/res/values-eu/strings.xml
deleted file mode 100644
index 9949ef0..0000000
--- a/go/res/values-eu/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Eduki sakatuta lasterbide bat aukeratzeko."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Sakatu birritan eta eduki sakatuta lasterbide bat aukeratzeko edo ekintza pertsonalizatuak erabiltzeko."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Lasterbideak"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> aplikazioaren lasterbidea"</string>
-</resources>
diff --git a/go/res/values-fa/strings.xml b/go/res/values-fa/strings.xml
deleted file mode 100644
index f1584d9..0000000
--- a/go/res/values-fa/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"برای انتخاب یک میان‌بر، لمس کنید و نگه‌دارید."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"برای انتخاب میان‌بر، دو ضربه سریع بزنید و نگه دارید یا از کنش‌های سفارشی استفاده کنید."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"میان‌برها"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"میان‌برهای <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-fi/strings.xml b/go/res/values-fi/strings.xml
deleted file mode 100644
index da9b0e1..0000000
--- a/go/res/values-fi/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Valitse pikakuvake painamalla sitä pitkään."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Valitse pikakuvake tai käytä muokattuja toimintoja kaksoisnapauttamalla ja painamalla pitkään."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Pikakuvakkeet"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Kohteen <xliff:g id="NAME">%1$s</xliff:g> pikakuvakkeet"</string>
-</resources>
diff --git a/go/res/values-fr-rCA/strings.xml b/go/res/values-fr-rCA/strings.xml
deleted file mode 100644
index c7fd6d6..0000000
--- a/go/res/values-fr-rCA/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Maintenez un doigt sur le raccourci pour l\'ajouter"</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Touchez 2x un raccourci et maintenez doigt dessus pour l’aj. ou utiliser des actions personnalisées."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Raccourcis"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Raccourcis : <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-fr/strings.xml b/go/res/values-fr/strings.xml
deleted file mode 100644
index 238fe73..0000000
--- a/go/res/values-fr/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Appui prolongé pour sélectionner raccourci."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Appuyez 2 fois et maintenez pression pour sélectionner raccourci ou utilisez actions personnalisées."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Raccourcis"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Raccourcis <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-gl-rES/strings.xml b/go/res/values-gl-rES/strings.xml
deleted file mode 100644
index 31621d5..0000000
--- a/go/res/values-gl-rES/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Mantén premido un atallo para seleccionalo."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Toca dúas veces e mantén premido para seleccionar un atallo ou utiliza accións personalizadas."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Atallos"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Atallos da aplicación <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-gl/strings.xml b/go/res/values-gl/strings.xml
deleted file mode 100644
index 31621d5..0000000
--- a/go/res/values-gl/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Mantén premido un atallo para seleccionalo."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Toca dúas veces e mantén premido para seleccionar un atallo ou utiliza accións personalizadas."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Atallos"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Atallos da aplicación <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-gu-rIN/strings.xml b/go/res/values-gu-rIN/strings.xml
deleted file mode 100644
index bdb549f..0000000
--- a/go/res/values-gu-rIN/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"એક શૉર્ટકટ ચૂંટવા ટૅપ કરી રાખો."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"એક શૉર્ટકટ ચૂંટવા અથવા કોઈ કસ્ટમ ક્રિયાઓનો ઉપયોગ કરવા માટે બે વાર ટૅપ કરી રાખો."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"શૉર્ટકટ"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> શૉર્ટકટ"</string>
-</resources>
diff --git a/go/res/values-gu/strings.xml b/go/res/values-gu/strings.xml
deleted file mode 100644
index bdb549f..0000000
--- a/go/res/values-gu/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"એક શૉર્ટકટ ચૂંટવા ટૅપ કરી રાખો."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"એક શૉર્ટકટ ચૂંટવા અથવા કોઈ કસ્ટમ ક્રિયાઓનો ઉપયોગ કરવા માટે બે વાર ટૅપ કરી રાખો."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"શૉર્ટકટ"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> શૉર્ટકટ"</string>
-</resources>
diff --git a/go/res/values-hi/strings.xml b/go/res/values-hi/strings.xml
deleted file mode 100644
index 2c1650a..0000000
--- a/go/res/values-hi/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"शॉर्टकट चुनने के लिए छूकर रखें."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"शॉर्टकट चुनने के लिए दो बार छूएं और कुछ देर दबाएं रखें या अपने मुताबिक कार्रवाइयों का इस्तेमाल करें."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"शॉर्टकट"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> शॉर्टकट"</string>
-</resources>
diff --git a/go/res/values-hr/strings.xml b/go/res/values-hr/strings.xml
deleted file mode 100644
index fccdeb5..0000000
--- a/go/res/values-hr/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Dodirnite i zadržite kako biste podigli prečac."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Dvaput dodirnite i zadržite pritisak kako biste podigli prečac ili pokušajte prilagođenim radnjama."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Prečaci"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Prečaci za aplikaciju <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-hu/strings.xml b/go/res/values-hu/strings.xml
deleted file mode 100644
index 369c227..0000000
--- a/go/res/values-hu/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Felvételhez tartsa nyomva a parancsikont."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Parancsikon felvételéhez koppintson rá duplán és tartsa nyomva, vagy használjon egyéni műveleteket."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Parancsikonok"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g>-parancsikonok"</string>
-</resources>
diff --git a/go/res/values-hy-rAM/strings.xml b/go/res/values-hy-rAM/strings.xml
deleted file mode 100644
index 4747f6d..0000000
--- a/go/res/values-hy-rAM/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Կրկնակի հպեք և պահեք՝ դյուրանցում ընտրելու համար։"</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Կրկնակի հպեք և պահեք՝ դյուրանցում ընտրելու համար կամ օգտվեք հարմարեցրած գործողություններից:"</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Դյուրանցումներ"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> դյուրանցումներ"</string>
-</resources>
diff --git a/go/res/values-hy/strings.xml b/go/res/values-hy/strings.xml
deleted file mode 100644
index 4747f6d..0000000
--- a/go/res/values-hy/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Կրկնակի հպեք և պահեք՝ դյուրանցում ընտրելու համար։"</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Կրկնակի հպեք և պահեք՝ դյուրանցում ընտրելու համար կամ օգտվեք հարմարեցրած գործողություններից:"</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Դյուրանցումներ"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> դյուրանցումներ"</string>
-</resources>
diff --git a/go/res/values-in/strings.xml b/go/res/values-in/strings.xml
deleted file mode 100644
index c8b9da3..0000000
--- a/go/res/values-in/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Tap lama untuk memilih pintasan."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Tap dua kali &amp; tahan untuk memilih pintasan atau menggunakan tindakan khusus."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Pintasan"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Pintasan <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-is-rIS/strings.xml b/go/res/values-is-rIS/strings.xml
deleted file mode 100644
index b8bb923..0000000
--- a/go/res/values-is-rIS/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Haltu fingri á flýtileið til að grípa hana."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Ý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="widget_button_text" msgid="4221900832360456858">"Flýtileiðir"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> flýtileiðir"</string>
-</resources>
diff --git a/go/res/values-is/strings.xml b/go/res/values-is/strings.xml
deleted file mode 100644
index b8bb923..0000000
--- a/go/res/values-is/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Haltu fingri á flýtileið til að grípa hana."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Ý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="widget_button_text" msgid="4221900832360456858">"Flýtileiðir"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> flýtileiðir"</string>
-</resources>
diff --git a/go/res/values-it/strings.xml b/go/res/values-it/strings.xml
deleted file mode 100644
index bc5d998..0000000
--- a/go/res/values-it/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Tocca e tieni premuto per scegliere la scorciatoia"</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Tocca due volte e tieni premuto per scegliere una scorciatoia o per usare azioni personalizzate."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Scorciatoie"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Scorciatoie di <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-iw/strings.xml b/go/res/values-iw/strings.xml
deleted file mode 100644
index f541d4d..0000000
--- a/go/res/values-iw/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"כדי להוסיף קיצור דרך, מקישים עליו פעמיים ומחזיקים."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"כדי להוסיף קיצור דרך או להשתמש בפעולות מותאמות אישית, מקישים על קיצור הדרך פעמיים ומחזיקים."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"קיצורי דרך"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"קיצורי דרך לאפליקציה <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-ja/strings.xml b/go/res/values-ja/strings.xml
deleted file mode 100644
index 8f02d7f..0000000
--- a/go/res/values-ja/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"ショートカットを追加するには押し続けます。"</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"ダブルタップ後に押し続けてショートカットを選択するか、カスタム操作を使用してください。"</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"ショートカット"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"「<xliff:g id="NAME">%1$s</xliff:g>」のショートカット"</string>
-</resources>
diff --git a/go/res/values-ka-rGE/strings.xml b/go/res/values-ka-rGE/strings.xml
deleted file mode 100644
index 1b46534..0000000
--- a/go/res/values-ka-rGE/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"შეეხეთ და დააყოვნეთ მალსახმობის ასარჩევად."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"ორმაგად შეეხეთ და გეჭიროთ მალსახმობის ასარჩევად ან მორგებული მოქმედებების გამოსაყენებლად."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"მალსახმობები"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g>-ის მალსახმობები"</string>
-</resources>
diff --git a/go/res/values-ka/strings.xml b/go/res/values-ka/strings.xml
deleted file mode 100644
index 1b46534..0000000
--- a/go/res/values-ka/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"შეეხეთ და დააყოვნეთ მალსახმობის ასარჩევად."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"ორმაგად შეეხეთ და გეჭიროთ მალსახმობის ასარჩევად ან მორგებული მოქმედებების გამოსაყენებლად."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"მალსახმობები"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g>-ის მალსახმობები"</string>
-</resources>
diff --git a/go/res/values-kk-rKZ/strings.xml b/go/res/values-kk-rKZ/strings.xml
deleted file mode 100644
index e909818..0000000
--- a/go/res/values-kk-rKZ/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Таңбашаны таңдау үшін оны түртіп, ұстап тұрыңыз."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Таңбашаны таңдау немесе арнаулы әрекеттерді пайдалану үшін екі рет түртіп, ұстап тұрыңыз."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Таңбашалар"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> таңбаша"</string>
-</resources>
diff --git a/go/res/values-kk/strings.xml b/go/res/values-kk/strings.xml
deleted file mode 100644
index e909818..0000000
--- a/go/res/values-kk/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Таңбашаны таңдау үшін оны түртіп, ұстап тұрыңыз."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Таңбашаны таңдау немесе арнаулы әрекеттерді пайдалану үшін екі рет түртіп, ұстап тұрыңыз."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Таңбашалар"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> таңбаша"</string>
-</resources>
diff --git a/go/res/values-km-rKH/strings.xml b/go/res/values-km-rKH/strings.xml
deleted file mode 100644
index 40082a4..0000000
--- a/go/res/values-km-rKH/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"ប៉ះ ហើយចុចឲ្យជាប់ដើម្បីរើសផ្លូវកាត់មួយ។"</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"ប៉ះពីរដង ហើយចុចឱ្យជាប់ដើម្បីរើសផ្លូវកាត់មួយ ឬប្រើសកម្មភាពផ្ទាល់ខ្លួន។"</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"ផ្លូវកាត់"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"ផ្លូវកាត់សម្រាប់ <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-km/strings.xml b/go/res/values-km/strings.xml
deleted file mode 100644
index 40082a4..0000000
--- a/go/res/values-km/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"ប៉ះ ហើយចុចឲ្យជាប់ដើម្បីរើសផ្លូវកាត់មួយ។"</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"ប៉ះពីរដង ហើយចុចឱ្យជាប់ដើម្បីរើសផ្លូវកាត់មួយ ឬប្រើសកម្មភាពផ្ទាល់ខ្លួន។"</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"ផ្លូវកាត់"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"ផ្លូវកាត់សម្រាប់ <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-kn-rIN/strings.xml b/go/res/values-kn-rIN/strings.xml
deleted file mode 100644
index 9c121fd..0000000
--- a/go/res/values-kn-rIN/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"ಸ್ಪರ್ಶಿಸಿ ಮತ್ತು ಶಾರ್ಟ್‌ಕಟ್ ಆರಿಸಲು ಹೋಲ್ಡ್ ಮಾಡಿ."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"ಡಬಲ್ ಟ್ಯಾಪ್ ಮಾಡಿ ಮತ್ತು ಶಾರ್ಟ್‌ಕಟ್ ಆರಿಸಿಕೊಳ್ಳಲು ಹೋಲ್ಡ್ ಮಾಡಿ ಅಥವಾ ಕಸ್ಟಮ್ ಕ್ರಿಯೆಗಳನ್ನು ಬಳಸಿ."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"ಶಾರ್ಟ್‌ಕಟ್‌ಗಳು"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> ಶಾರ್ಟ್‌ಕಟ್‌ಗಳು"</string>
-</resources>
diff --git a/go/res/values-kn/strings.xml b/go/res/values-kn/strings.xml
deleted file mode 100644
index 9c121fd..0000000
--- a/go/res/values-kn/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"ಸ್ಪರ್ಶಿಸಿ ಮತ್ತು ಶಾರ್ಟ್‌ಕಟ್ ಆರಿಸಲು ಹೋಲ್ಡ್ ಮಾಡಿ."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"ಡಬಲ್ ಟ್ಯಾಪ್ ಮಾಡಿ ಮತ್ತು ಶಾರ್ಟ್‌ಕಟ್ ಆರಿಸಿಕೊಳ್ಳಲು ಹೋಲ್ಡ್ ಮಾಡಿ ಅಥವಾ ಕಸ್ಟಮ್ ಕ್ರಿಯೆಗಳನ್ನು ಬಳಸಿ."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"ಶಾರ್ಟ್‌ಕಟ್‌ಗಳು"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> ಶಾರ್ಟ್‌ಕಟ್‌ಗಳು"</string>
-</resources>
diff --git a/go/res/values-ko/strings.xml b/go/res/values-ko/strings.xml
deleted file mode 100644
index 60f925e..0000000
--- a/go/res/values-ko/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"바로가기를 선택하려면 길게 터치하세요."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"바로가기를 선택하려면 두 번 탭한 다음 길게 터치하거나 맞춤 액션을 사용합니다."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"바로가기"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 바로가기"</string>
-</resources>
diff --git a/go/res/values-ky-rKG/strings.xml b/go/res/values-ky-rKG/strings.xml
deleted file mode 100644
index 4c7e973..0000000
--- a/go/res/values-ky-rKG/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Кыска жолду тандоо үчүн басып туруңуз."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Кыска жолду тандоо үчүн эки жолу таптап, кармап туруңуз же ыңгайлаштырылган аракеттерди колдонуңуз."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Кыска жолдор"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> кыска жол"</string>
-</resources>
diff --git a/go/res/values-ky/strings.xml b/go/res/values-ky/strings.xml
deleted file mode 100644
index 4c7e973..0000000
--- a/go/res/values-ky/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Кыска жолду тандоо үчүн басып туруңуз."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Кыска жолду тандоо үчүн эки жолу таптап, кармап туруңуз же ыңгайлаштырылган аракеттерди колдонуңуз."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Кыска жолдор"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> кыска жол"</string>
-</resources>
diff --git a/go/res/values-lo-rLA/strings.xml b/go/res/values-lo-rLA/strings.xml
deleted file mode 100644
index 7864884..0000000
--- a/go/res/values-lo-rLA/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"ແຕະຄ້າງໄວ້ເພື່ອຮັບປຸ່ມລັດ."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"ແຕະສອງເທື່ອຄ້າງໄວ້ເພື່ອຮັບປຸ່ມລັດ ຫຼື ໃຊ້ຄຳສັ່ງແບບກຳນົດເອງ."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"ປຸ່ມລັດ"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"ປຸ່ມລັດ <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-lo/strings.xml b/go/res/values-lo/strings.xml
deleted file mode 100644
index 7864884..0000000
--- a/go/res/values-lo/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"ແຕະຄ້າງໄວ້ເພື່ອຮັບປຸ່ມລັດ."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"ແຕະສອງເທື່ອຄ້າງໄວ້ເພື່ອຮັບປຸ່ມລັດ ຫຼື ໃຊ້ຄຳສັ່ງແບບກຳນົດເອງ."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"ປຸ່ມລັດ"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"ປຸ່ມລັດ <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-lt/strings.xml b/go/res/values-lt/strings.xml
deleted file mode 100644
index 8f49032..0000000
--- a/go/res/values-lt/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Dukart pal. ir palaik., kad pasir. spart. klav."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Dukart palieskite ir palaikykite, kad pasirinkt. spartųjį klavišą ar naudotumėte tinkintus veiksmus."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Spartieji klavišai"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"„<xliff:g id="NAME">%1$s</xliff:g>“ spartieji klavišai"</string>
-</resources>
diff --git a/go/res/values-lv/strings.xml b/go/res/values-lv/strings.xml
deleted file mode 100644
index 04315eb..0000000
--- a/go/res/values-lv/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Lai izvēlētos saīsni, pieskarieties un turiet to."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Lai atlasītu saīsni, veiciet dubultskārienu uz tās un turiet to vai arī veiciet pielāgotas darbības."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Saīsnes"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Lietotnes <xliff:g id="NAME">%1$s</xliff:g> saīsnes"</string>
-</resources>
diff --git a/go/res/values-mk-rMK/strings.xml b/go/res/values-mk-rMK/strings.xml
deleted file mode 100644
index 52d66b5..0000000
--- a/go/res/values-mk-rMK/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Допрете двапати и задржете за да изберете кратенка."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Допрете двапати и задржете за да изберете кратенка или да користите приспособени дејства."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Кратенки"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Кратенки за <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-mk/strings.xml b/go/res/values-mk/strings.xml
deleted file mode 100644
index 52d66b5..0000000
--- a/go/res/values-mk/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Допрете двапати и задржете за да изберете кратенка."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Допрете двапати и задржете за да изберете кратенка или да користите приспособени дејства."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Кратенки"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Кратенки за <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-ml-rIN/strings.xml b/go/res/values-ml-rIN/strings.xml
deleted file mode 100644
index b3c12e1..0000000
--- a/go/res/values-ml-rIN/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"ഒരു കുറുക്കുവഴി ചേർക്കുന്നതിന് അത് സ്‌പർശിച്ച് പിടിക്കുക."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"ഒരു കുറുക്കുവഴി തിരഞ്ഞെടുക്കാനോ ഇഷ്ടാനുസൃത പ്രവർത്തനങ്ങൾ ഉപയോഗിക്കാനോ രണ്ടുതവണ ടാപ്പുചെയ്ത് പിടിക്കുക."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"കുറുക്കുവഴികൾ"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> കുറുക്കുവഴികൾ"</string>
-</resources>
diff --git a/go/res/values-ml/strings.xml b/go/res/values-ml/strings.xml
deleted file mode 100644
index b3c12e1..0000000
--- a/go/res/values-ml/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"ഒരു കുറുക്കുവഴി ചേർക്കുന്നതിന് അത് സ്‌പർശിച്ച് പിടിക്കുക."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"ഒരു കുറുക്കുവഴി തിരഞ്ഞെടുക്കാനോ ഇഷ്ടാനുസൃത പ്രവർത്തനങ്ങൾ ഉപയോഗിക്കാനോ രണ്ടുതവണ ടാപ്പുചെയ്ത് പിടിക്കുക."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"കുറുക്കുവഴികൾ"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> കുറുക്കുവഴികൾ"</string>
-</resources>
diff --git a/go/res/values-mn-rMN/strings.xml b/go/res/values-mn-rMN/strings.xml
deleted file mode 100644
index c89dfd1..0000000
--- a/go/res/values-mn-rMN/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Товчлол авах бол удаан дарна уу."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Товчлол авах болон тохируулсан үйлдлийг ашиглахын тулд хоёр товшоод хүлээнэ үү."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Товчлол"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g>-н товчлол"</string>
-</resources>
diff --git a/go/res/values-mn/strings.xml b/go/res/values-mn/strings.xml
deleted file mode 100644
index c89dfd1..0000000
--- a/go/res/values-mn/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Товчлол авах бол удаан дарна уу."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Товчлол авах болон тохируулсан үйлдлийг ашиглахын тулд хоёр товшоод хүлээнэ үү."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Товчлол"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g>-н товчлол"</string>
-</resources>
diff --git a/go/res/values-mr-rIN/strings.xml b/go/res/values-mr-rIN/strings.xml
deleted file mode 100644
index 2c767b4..0000000
--- a/go/res/values-mr-rIN/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"शॉर्टकट निवडण्यासाठी स्पर्श करा आणि धरून ठेवा."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"शॉर्टकट निवडण्यासाठी किंवा कस्टम क्रिया वापरण्यासाठी दोनदा टॅप करा आणि धरून ठेवा."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"शॉर्टकट"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> शॉर्टकट"</string>
-</resources>
diff --git a/go/res/values-mr/strings.xml b/go/res/values-mr/strings.xml
deleted file mode 100644
index 2c767b4..0000000
--- a/go/res/values-mr/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"शॉर्टकट निवडण्यासाठी स्पर्श करा आणि धरून ठेवा."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"शॉर्टकट निवडण्यासाठी किंवा कस्टम क्रिया वापरण्यासाठी दोनदा टॅप करा आणि धरून ठेवा."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"शॉर्टकट"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> शॉर्टकट"</string>
-</resources>
diff --git a/go/res/values-ms-rMY/strings.xml b/go/res/values-ms-rMY/strings.xml
deleted file mode 100644
index 42add9a..0000000
--- a/go/res/values-ms-rMY/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Sentuh &amp; tahan untuk mengambil pintasan."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Ketik dua kali &amp; tahan untuk mengambil pintasan atau menggunakan tindakan tersuai."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Pintasan"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Pintasan <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-ms/strings.xml b/go/res/values-ms/strings.xml
deleted file mode 100644
index 42add9a..0000000
--- a/go/res/values-ms/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Sentuh &amp; tahan untuk mengambil pintasan."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Ketik dua kali &amp; tahan untuk mengambil pintasan atau menggunakan tindakan tersuai."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Pintasan"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Pintasan <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-my-rMM/strings.xml b/go/res/values-my-rMM/strings.xml
deleted file mode 100644
index 5784df6..0000000
--- a/go/res/values-my-rMM/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"လက်ကွက်ဖြတ်လမ်းတစ်ခုကို ရွေးရန် ထိပြီး ဖိထားပါ"</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"လက်ကွက်ဖြတ်လမ်းကို ရွေးရန် (သို့) စိတ်ကြိုက်လုပ်ဆောင်ချက်များကို သုံးရန်နှစ်ချက်တို့ပြီး ဖိထားပါ။"</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"ဖြတ်လမ်းလင့်ခ်များ"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> ဖြတ်လမ်းလင့်ခ်များ"</string>
-</resources>
diff --git a/go/res/values-my/strings.xml b/go/res/values-my/strings.xml
deleted file mode 100644
index 5784df6..0000000
--- a/go/res/values-my/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"လက်ကွက်ဖြတ်လမ်းတစ်ခုကို ရွေးရန် ထိပြီး ဖိထားပါ"</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"လက်ကွက်ဖြတ်လမ်းကို ရွေးရန် (သို့) စိတ်ကြိုက်လုပ်ဆောင်ချက်များကို သုံးရန်နှစ်ချက်တို့ပြီး ဖိထားပါ။"</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"ဖြတ်လမ်းလင့်ခ်များ"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> ဖြတ်လမ်းလင့်ခ်များ"</string>
-</resources>
diff --git a/go/res/values-nb/strings.xml b/go/res/values-nb/strings.xml
deleted file mode 100644
index 2a5ffb6..0000000
--- a/go/res/values-nb/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Trykk og hold for å velge en snarvei."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Dobbelttrykk og hold for å velge en snarvei eller bruke tilpassede handlinger."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Snarveier"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g>-snarveier"</string>
-</resources>
diff --git a/go/res/values-ne-rNP/strings.xml b/go/res/values-ne-rNP/strings.xml
deleted file mode 100644
index 0be0375..0000000
--- a/go/res/values-ne-rNP/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"कुनै सटकर्ट छनौट गर्न छोइराख्नुहोस्।"</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"कुनै सर्टकट छनौट गर्न दुईपटक ट्याप गरेर होल्ड गर्नुहोस् वा रोजेका कारबाहीहरू प्रयोग गर्नुहोस्।"</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"सर्टकटहरू"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> सर्टकटहरू"</string>
-</resources>
diff --git a/go/res/values-ne/strings.xml b/go/res/values-ne/strings.xml
deleted file mode 100644
index 0be0375..0000000
--- a/go/res/values-ne/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"कुनै सटकर्ट छनौट गर्न छोइराख्नुहोस्।"</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"कुनै सर्टकट छनौट गर्न दुईपटक ट्याप गरेर होल्ड गर्नुहोस् वा रोजेका कारबाहीहरू प्रयोग गर्नुहोस्।"</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"सर्टकटहरू"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> सर्टकटहरू"</string>
-</resources>
diff --git a/go/res/values-nl/strings.xml b/go/res/values-nl/strings.xml
deleted file mode 100644
index 5bcd016..0000000
--- a/go/res/values-nl/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Tik en houd vast om snelkoppeling toe te voegen."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Dubbeltik en houd vast om een snelkoppeling toe te voegen of aangepaste acties te gebruiken."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Snelkoppelingen"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g>-snelkoppelingen"</string>
-</resources>
diff --git a/go/res/values-or/strings.xml b/go/res/values-or/strings.xml
deleted file mode 100644
index 3ec8a72..0000000
--- a/go/res/values-or/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"ଏକ ଶର୍ଟକଟ୍ ଚୟନ କରିବାକୁ ଦାବି ଧରନ୍ତୁ।"</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"ଡବଲ୍‌-ଟାପ୍‌ କରନ୍ତୁ ଏବଂ ଏକ ଶର୍ଟକଟ୍ ଚୟନ କରିବାକୁ ଧରି ରଖନ୍ତୁ କିମ୍ୱା କଷ୍ଟମ୍ ପ୍ରକ୍ରିୟା ବ୍ୟବହାର କରନ୍ତୁ।"</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"ଶର୍ଟକଟ୍‍"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g>ର ଶର୍ଟକଟ୍"</string>
-</resources>
diff --git a/go/res/values-pa-rIN/strings.xml b/go/res/values-pa-rIN/strings.xml
deleted file mode 100644
index c7e4abf..0000000
--- a/go/res/values-pa-rIN/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"ਕੋਈ ਸ਼ਾਰਟਕੱਟ ਚੁਣਨ ਲਈ ਸਪੱਰਸ਼ ਕਰੋ ਅਤੇ ਦਬਾ ਕੇ ਰੱਖੋ।"</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"ਕੋਈ ਸ਼ਾਰਟਕੱਟ ਚੁਣਨ ਲਈ ਦੋ ਵਾਰ ਟੈਪ ਕਰੋ ਅਤੇ ਦਬਾ ਕੇ ਰੱਖੋ ਜਾਂ ਵਿਉਂਂਤੀ ਕਾਰਵਾਈਆਂ ਵਰਤੋ।"</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"ਸ਼ਾਰਟਕੱਟ"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> ਸ਼ਾਰਟਕੱਟ"</string>
-</resources>
diff --git a/go/res/values-pa/strings.xml b/go/res/values-pa/strings.xml
deleted file mode 100644
index c7e4abf..0000000
--- a/go/res/values-pa/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"ਕੋਈ ਸ਼ਾਰਟਕੱਟ ਚੁਣਨ ਲਈ ਸਪੱਰਸ਼ ਕਰੋ ਅਤੇ ਦਬਾ ਕੇ ਰੱਖੋ।"</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"ਕੋਈ ਸ਼ਾਰਟਕੱਟ ਚੁਣਨ ਲਈ ਦੋ ਵਾਰ ਟੈਪ ਕਰੋ ਅਤੇ ਦਬਾ ਕੇ ਰੱਖੋ ਜਾਂ ਵਿਉਂਂਤੀ ਕਾਰਵਾਈਆਂ ਵਰਤੋ।"</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"ਸ਼ਾਰਟਕੱਟ"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> ਸ਼ਾਰਟਕੱਟ"</string>
-</resources>
diff --git a/go/res/values-pl/strings.xml b/go/res/values-pl/strings.xml
deleted file mode 100644
index 45a1dc2..0000000
--- a/go/res/values-pl/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Kliknij i przytrzymaj, by wybrać skrót."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Kliknij dwukrotnie i przytrzymaj, by wybrać skrót lub użyć działań niestandardowych."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Skróty"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> – skróty"</string>
-</resources>
diff --git a/go/res/values-pt-rPT/strings.xml b/go/res/values-pt-rPT/strings.xml
deleted file mode 100644
index 7a75a05..0000000
--- a/go/res/values-pt-rPT/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Toque sem soltar para escolher um atalho."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Toque duas vezes sem soltar para escolher um atalho ou utilize ações personalizadas."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Atalhos"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Atalhos da aplicação <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-pt/strings.xml b/go/res/values-pt/strings.xml
deleted file mode 100644
index 53bbfc4..0000000
--- a/go/res/values-pt/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Toque e segure para selecionar um atalho."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Toque duas vezes na tela e segure para selecionar um atalho ou usar ações personalizadas."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Atalhos"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Atalhos do app <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-ro/strings.xml b/go/res/values-ro/strings.xml
deleted file mode 100644
index 75d1796..0000000
--- a/go/res/values-ro/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Atingeți și țineți apăsat pentru a selecta o comandă rapidă."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Atingeți de două ori și țineți apăsat pentru comandă rapidă sau folosiți acțiuni personalizate."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Comenzi rapide"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Comenzi rapide pentru <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-ru/strings.xml b/go/res/values-ru/strings.xml
deleted file mode 100644
index 9c5c8cd..0000000
--- a/go/res/values-ru/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Чтобы выбрать ярлык, нажмите на него и удерживайте."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Чтобы выбрать ярлык или использовать специальные действия, нажмите на него дважды и не отпускайте."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Ярлыки"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g>: ярлыки"</string>
-</resources>
diff --git a/go/res/values-si-rLK/strings.xml b/go/res/values-si-rLK/strings.xml
deleted file mode 100644
index 4b25c90..0000000
--- a/go/res/values-si-rLK/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"කෙටි මගක් තෝරා ගැනීමට ස්පර්ශ කර අල්ලාගෙන සිටින්න."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"විජට් එකක් තෝරා ගැනීමට හෝ අභිරුචි භාවිත කිරීමට දෙවරක් තට්ටු කර අල්ලා ගෙන සිටින්න."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"කෙටි මං"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"කෙටි මං <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-si/strings.xml b/go/res/values-si/strings.xml
deleted file mode 100644
index 4b25c90..0000000
--- a/go/res/values-si/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"කෙටි මගක් තෝරා ගැනීමට ස්පර්ශ කර අල්ලාගෙන සිටින්න."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"විජට් එකක් තෝරා ගැනීමට හෝ අභිරුචි භාවිත කිරීමට දෙවරක් තට්ටු කර අල්ලා ගෙන සිටින්න."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"කෙටි මං"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"කෙටි මං <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-sk/strings.xml b/go/res/values-sk/strings.xml
deleted file mode 100644
index fc02933..0000000
--- a/go/res/values-sk/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Skratku pridáte pridržaním."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Skratku pridáte dvojitým klepnutím a pridržaním alebo pomocou vlastných akcií."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Skratky"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Skratky aplikácie <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-sl/strings.xml b/go/res/values-sl/strings.xml
deleted file mode 100644
index 6ecedfb..0000000
--- a/go/res/values-sl/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Pridržite bližnjico, da jo izberete."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Dvakrat se dotaknite bližnjice in jo pridržite, da jo izberete, ali pa uporabite dejanja po meri."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Bližnjice"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Bližnjice za <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-sq-rAL/strings.xml b/go/res/values-sq-rAL/strings.xml
deleted file mode 100644
index bb74db6..0000000
--- a/go/res/values-sq-rAL/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Prek dhe mbaj prekur për të zgjedhur një shkurtore."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Prek dy herë dhe mbaj prekur për të zgjedhur një shkurtore ose për të përdorur veprimet e personalizuara."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Shkurtoret"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> shkurtore"</string>
-</resources>
diff --git a/go/res/values-sq/strings.xml b/go/res/values-sq/strings.xml
deleted file mode 100644
index bb74db6..0000000
--- a/go/res/values-sq/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Prek dhe mbaj prekur për të zgjedhur një shkurtore."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Prek dy herë dhe mbaj prekur për të zgjedhur një shkurtore ose për të përdorur veprimet e personalizuara."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Shkurtoret"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> shkurtore"</string>
-</resources>
diff --git a/go/res/values-sr/strings.xml b/go/res/values-sr/strings.xml
deleted file mode 100644
index 0b9aea2..0000000
--- a/go/res/values-sr/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Додирните и задржите да бисте изабрали пречицу."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Двапут додирните и задржите да бисте изабрали пречицу или користите прилагођене радње."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Пречице"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Пречице за <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-sv/strings.xml b/go/res/values-sv/strings.xml
deleted file mode 100644
index c3f434c..0000000
--- a/go/res/values-sv/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Tryck länge om du vill ta upp en genväg."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"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="widget_button_text" msgid="4221900832360456858">"Genvägar"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Genvägar för <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-sw/strings.xml b/go/res/values-sw/strings.xml
deleted file mode 100644
index 13c12e4..0000000
--- a/go/res/values-sw/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Gusa na ushikilie ili uchague njia ya mkato."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Gusa mara mbili na ushikilie ile uchague njia ya mkato au utumie vitendo maalum."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Njia za mkato"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Njia za mkato za <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-ta-rIN/strings.xml b/go/res/values-ta-rIN/strings.xml
deleted file mode 100644
index 50059b6..0000000
--- a/go/res/values-ta-rIN/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"குறுக்குவழியைச் சேர்க்க, தொட்டு பிடித்திருக்கவும்."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"குறுக்குவழியை சேர்க்க, இருமுறை தட்டிப் பிடித்திருக்கவும் அல்லது தனிப்பயன் செயல்களைப் பயன்படுத்தவும்."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"குறுக்குவழிகள்"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> குறுக்குவழிகள்"</string>
-</resources>
diff --git a/go/res/values-ta/strings.xml b/go/res/values-ta/strings.xml
deleted file mode 100644
index 50059b6..0000000
--- a/go/res/values-ta/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"குறுக்குவழியைச் சேர்க்க, தொட்டு பிடித்திருக்கவும்."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"குறுக்குவழியை சேர்க்க, இருமுறை தட்டிப் பிடித்திருக்கவும் அல்லது தனிப்பயன் செயல்களைப் பயன்படுத்தவும்."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"குறுக்குவழிகள்"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> குறுக்குவழிகள்"</string>
-</resources>
diff --git a/go/res/values-te-rIN/strings.xml b/go/res/values-te-rIN/strings.xml
deleted file mode 100644
index 0bdf743..0000000
--- a/go/res/values-te-rIN/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"సత్వరమార్గాన్ని ఎంచుకోవడానికి తాకి &amp; నొక్కి ఉంచండి."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"సత్వరమార్గాన్ని ఎంచుకోవడానికి లేదా అనుకూల చర్యలను ఉపయోగించడానికి రెండుసార్లు నొక్కి &amp;ఉంచండి."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"సత్వరమార్గాలు"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> సత్వరమార్గాలు"</string>
-</resources>
diff --git a/go/res/values-te/strings.xml b/go/res/values-te/strings.xml
deleted file mode 100644
index 0bdf743..0000000
--- a/go/res/values-te/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"సత్వరమార్గాన్ని ఎంచుకోవడానికి తాకి &amp; నొక్కి ఉంచండి."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"సత్వరమార్గాన్ని ఎంచుకోవడానికి లేదా అనుకూల చర్యలను ఉపయోగించడానికి రెండుసార్లు నొక్కి &amp;ఉంచండి."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"సత్వరమార్గాలు"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> సత్వరమార్గాలు"</string>
-</resources>
diff --git a/go/res/values-th/strings.xml b/go/res/values-th/strings.xml
deleted file mode 100644
index e73d89f..0000000
--- a/go/res/values-th/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"แตะค้างไว้เพื่อเลือกทางลัด"</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"แตะสองครั้งค้างไว้เพื่อเลือกทางลัดหรือใช้การกระทำที่กำหนดเอง"</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"ทางลัด"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"ทางลัด <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-tl/strings.xml b/go/res/values-tl/strings.xml
deleted file mode 100644
index 8f44ec5..0000000
--- a/go/res/values-tl/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Pindutin nang matagal upang kumuha ng shortcut."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"I-double tap nang matagal upang kumuha ng shortcut o gumamit ng mga custom na pagkilos."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Mga Shortcut"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Mga shortcut sa <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-tr/strings.xml b/go/res/values-tr/strings.xml
deleted file mode 100644
index f0f3cce..0000000
--- a/go/res/values-tr/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Kısayol seçmek için dokunun ve basılı tutun."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Bir kısayolu seçmek veya özel işlemleri kullanmak için iki kez dokunun ve basılı tutun."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Kısayollar"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> kısayolları"</string>
-</resources>
diff --git a/go/res/values-uk/strings.xml b/go/res/values-uk/strings.xml
deleted file mode 100644
index 8d1f583..0000000
--- a/go/res/values-uk/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Натисніть і утримуйте, щоб вибрати ярлик."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Двічі натисніть і утримуйте, щоб вибрати ярлик, або виконайте іншу дію."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Ярлики"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Ярлики додатка <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-ur-rPK/strings.xml b/go/res/values-ur-rPK/strings.xml
deleted file mode 100644
index 46bd823..0000000
--- a/go/res/values-ur-rPK/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"کوئی شارٹ کٹ منتخب کرنے کیلئے ٹچ کریں اور دبائے رکھیں۔"</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"کوئی شارٹ کٹ منتخب کرنے یا حسب ضرورت کاروائیاں استعمال کرنے کیلئے دو بار تھپتھپائیں اور دبائے رکھیں۔"</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"شارٹ کٹس"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> شارٹ کٹس"</string>
-</resources>
diff --git a/go/res/values-ur/strings.xml b/go/res/values-ur/strings.xml
deleted file mode 100644
index 46bd823..0000000
--- a/go/res/values-ur/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"کوئی شارٹ کٹ منتخب کرنے کیلئے ٹچ کریں اور دبائے رکھیں۔"</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"کوئی شارٹ کٹ منتخب کرنے یا حسب ضرورت کاروائیاں استعمال کرنے کیلئے دو بار تھپتھپائیں اور دبائے رکھیں۔"</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"شارٹ کٹس"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> شارٹ کٹس"</string>
-</resources>
diff --git a/go/res/values-uz-rUZ/strings.xml b/go/res/values-uz-rUZ/strings.xml
deleted file mode 100644
index 318bc15..0000000
--- a/go/res/values-uz-rUZ/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Yorliqni tanlab olish uchun bosib turing."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Ikki marta bosib va bosib turgan holatda yorliqni tanlang yoki maxsus amaldan foydalaning."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Yorliqlar"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> ilovasi yorliqlari"</string>
-</resources>
diff --git a/go/res/values-uz/strings.xml b/go/res/values-uz/strings.xml
deleted file mode 100644
index 318bc15..0000000
--- a/go/res/values-uz/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Yorliqni tanlab olish uchun bosib turing."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Ikki marta bosib va bosib turgan holatda yorliqni tanlang yoki maxsus amaldan foydalaning."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Yorliqlar"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> ilovasi yorliqlari"</string>
-</resources>
diff --git a/go/res/values-vi/strings.xml b/go/res/values-vi/strings.xml
deleted file mode 100644
index 1197619..0000000
--- a/go/res/values-vi/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Chạm và giữ để chọn lối tắt."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"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="widget_button_text" msgid="4221900832360456858">"Lối tắt"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Lối tắt <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-zh-rCN/strings.xml b/go/res/values-zh-rCN/strings.xml
deleted file mode 100644
index 57351d3..0000000
--- a/go/res/values-zh-rCN/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"触摸并按住快捷方式即可选择快捷方式。"</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"点按两次并按住快捷方式即可选择快捷方式,您也可以使用自定义操作。"</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"快捷方式"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g>快捷方式"</string>
-</resources>
diff --git a/go/res/values-zh-rHK/strings.xml b/go/res/values-zh-rHK/strings.xml
deleted file mode 100644
index dea7749..0000000
--- a/go/res/values-zh-rHK/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
-</resources>
diff --git a/go/res/values-zh-rTW/strings.xml b/go/res/values-zh-rTW/strings.xml
deleted file mode 100644
index 07ae2ed..0000000
--- a/go/res/values-zh-rTW/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取。"</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"輕觸兩下並按住捷徑即可選取,你也可以使用自訂動作。"</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"「<xliff:g id="NAME">%1$s</xliff:g>」捷徑"</string>
-</resources>
diff --git a/go/res/values-zu/strings.xml b/go/res/values-zu/strings.xml
deleted file mode 100644
index e5051df..0000000
--- a/go/res/values-zu/strings.xml
+++ /dev/null
@@ -1,26 +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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="long_press_widget_to_add" msgid="4001616142797446267">"Thinta futhi ubambe ukuze ukhethe isinqamuleli."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Thepha kabulu futhi ubambe ukuze ukhethe isinqamuleli noma usebenzise izenzo zangokwezifiso."</string>
-    <string name="widget_button_text" msgid="4221900832360456858">"Izinqamuleli"</string>
-    <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> izinqamuleli"</string>
-</resources>
diff --git a/go/res/values/strings.xml b/go/res/values/strings.xml
deleted file mode 100644
index 8ef2e62..0000000
--- a/go/res/values/strings.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.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- Message to tell the user to press and hold on a shortcut to add it [CHAR_LIMIT=50] -->
-    <string name="long_press_widget_to_add">Touch &amp; hold to pick up a shortcut.</string>
-    <!-- Accessibility spoken hint message in widget picker, which allows user to add a shortcut. 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 shortcut or use custom actions.</string>
-    <!-- Text for shortcut add button -->
-    <string name="widget_button_text">Shortcuts</string>
-
-    <!-- Strings for widgets & more in the popup container/bottom sheet -->
-    <!-- Title for a bottom sheet that shows shortcuts for a particular app -->
-    <string name="widgets_bottom_sheet_title"><xliff:g id="name" example="Messenger">%1$s</xliff:g> shortcuts</string>
-
-</resources>
diff --git a/go/res/xml/device_profiles.xml b/go/res/xml/device_profiles.xml
index 16d7e13..0fd0eeb 100644
--- a/go/res/xml/device_profiles.xml
+++ b/go/res/xml/device_profiles.xml
@@ -17,17 +17,23 @@
 
 <profiles xmlns:launcher="http://schemas.android.com/apk/res-auto" >
 
-    <profile
-        launcher:name="Go Device"
-        launcher:minWidthDps="296"
-        launcher:minHeightDps="491.33"
+    <grid-option
+        launcher:name="4_by_4"
         launcher:numRows="4"
         launcher:numColumns="4"
         launcher:numFolderRows="4"
         launcher:numFolderColumns="4"
-        launcher:iconSize="60"
-        launcher:iconTextSize="14.0"
-        launcher:defaultLayoutId="@xml/default_workspace_4x4"
-        />
+        launcher:numHotseatIcons="4"
+        launcher:defaultLayoutId="@xml/default_workspace_4x4" >
+
+        <display-option
+            launcher:name="Go Device"
+            launcher:minWidthDps="296"
+            launcher:minHeightDps="491.33"
+            launcher:iconImageSize="60"
+            launcher:iconTextSize="14.0"
+            launcher:canBeDefault="true" />
+
+    </grid-option>
 
 </profiles>
\ No newline at end of file
diff --git a/go/src/com/android/launcher3/config/FeatureFlags.java b/go/src/com/android/launcher3/config/FeatureFlags.java
new file mode 100644
index 0000000..a90808c
--- /dev/null
+++ b/go/src/com/android/launcher3/config/FeatureFlags.java
@@ -0,0 +1,32 @@
+/*
+ * 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.config;
+
+import android.content.Context;
+
+/**
+ * Defines a set of flags used to control various launcher behaviors
+ */
+public final class FeatureFlags extends BaseFlags {
+    private FeatureFlags() {
+        // Prevent instantiation
+    }
+
+    // Features to control Launcher3Go behavior
+    public static final boolean GO_DISABLE_WIDGETS = true;
+    public static final boolean LAUNCHER3_SPRING_ICONS = false;
+}
diff --git a/go/src/com/android/launcher3/model/LoaderResults.java b/go/src/com/android/launcher3/model/LoaderResults.java
new file mode 100644
index 0000000..b82f362
--- /dev/null
+++ b/go/src/com/android/launcher3/model/LoaderResults.java
@@ -0,0 +1,42 @@
+/*
+ * 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.model;
+
+import com.android.launcher3.AllAppsList;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel.Callbacks;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Helper class to handle results of {@link com.android.launcher3.model.LoaderTask}.
+ */
+public class LoaderResults extends BaseLoaderResults {
+
+    public LoaderResults(LauncherAppState app, BgDataModel dataModel,
+            AllAppsList allAppsList, int pageToBindFirst, WeakReference<Callbacks> callbacks) {
+        super(app, dataModel, allAppsList, pageToBindFirst, callbacks);
+    }
+
+    @Override
+    public void bindDeepShortcuts() {
+    }
+
+    @Override
+    public void bindWidgets() {
+    }
+}
diff --git a/go/src/com/android/launcher3/model/WidgetsModel.java b/go/src/com/android/launcher3/model/WidgetsModel.java
new file mode 100644
index 0000000..18f3f9d
--- /dev/null
+++ b/go/src/com/android/launcher3/model/WidgetsModel.java
@@ -0,0 +1,67 @@
+/*
+ * 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.model;
+
+import android.content.Context;
+import android.os.UserHandle;
+
+import com.android.launcher3.icons.ComponentWithLabel;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.widget.WidgetListRowEntry;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Widgets data model that is used by the adapters of the widget views and controllers.
+ *
+ * <p> The widgets and shortcuts are organized using package name as its index.
+ */
+public class WidgetsModel {
+    private static final ArrayList<WidgetListRowEntry> 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}
+     *
+     * @see com.android.launcher3.widget.WidgetsListAdapter#setWidgets(ArrayList)
+     */
+    public synchronized ArrayList<WidgetListRowEntry> getWidgetsList(Context context) {
+        return EMPTY_WIDGET_LIST;
+    }
+
+    /**
+     * @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise
+     *                    only widgets and shortcuts associated with the package/user are.
+     */
+    public List<ComponentWithLabel> update(LauncherAppState app,
+            @Nullable PackageUserKey packageUser) {
+        return Collections.emptyList();
+    }
+
+
+    public void onPackageIconsUpdated(Set<String> packageNames, UserHandle user,
+            LauncherAppState app) {
+    }
+}
\ No newline at end of file
diff --git a/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java b/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
new file mode 100644
index 0000000..1e44910
--- /dev/null
+++ b/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
@@ -0,0 +1,125 @@
+/*
+ * 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.shortcuts;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ShortcutInfo;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.UserHandle;
+
+import com.android.launcher3.ItemInfo;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Performs operations related to deep shortcuts, such as querying for them, pinning them, etc.
+ */
+public class DeepShortcutManager {
+    private static DeepShortcutManager sInstance;
+    private static final Object sInstanceLock = new Object();
+
+    public static DeepShortcutManager getInstance(Context context) {
+        synchronized (sInstanceLock) {
+            if (sInstance == null) {
+                sInstance = new DeepShortcutManager(context.getApplicationContext());
+            }
+            return sInstance;
+        }
+    }
+
+    private DeepShortcutManager(Context context) {
+    }
+
+    public static boolean supportsShortcuts(ItemInfo info) {
+        return false;
+    }
+
+    public boolean wasLastCallSuccess() {
+        return false;
+    }
+
+    public void onShortcutsChanged(List<ShortcutInfo> shortcuts) {
+    }
+
+    /**
+     * Queries for the shortcuts with the package name and provided ids.
+     *
+     * This method is intended to get the full details for shortcuts when they are added or updated,
+     * because we only get "key" fields in onShortcutsChanged().
+     */
+    public List<ShortcutInfo> queryForFullDetails(String packageName,
+            List<String> shortcutIds, UserHandle user) {
+        return Collections.emptyList();
+    }
+
+    /**
+     * Gets all the manifest and dynamic shortcuts associated with the given package and user,
+     * to be displayed in the shortcuts container on long press.
+     */
+    public List<ShortcutInfo> queryForShortcutsContainer(ComponentName activity,
+            UserHandle user) {
+        return Collections.emptyList();
+    }
+
+    /**
+     * Removes the given shortcut from the current list of pinned shortcuts.
+     * (Runs on background thread)
+     */
+    public void unpinShortcut(final ShortcutKey key) {
+    }
+
+    /**
+     * Adds the given shortcut to the current list of pinned shortcuts.
+     * (Runs on background thread)
+     */
+    public void pinShortcut(final ShortcutKey key) {
+    }
+
+    public void startShortcut(String packageName, String id, Rect sourceBounds,
+            Bundle startActivityOptions, UserHandle user) {
+    }
+
+    public Drawable getShortcutIconDrawable(ShortcutInfo shortcutInfo, int density) {
+        return null;
+    }
+
+    /**
+     * Returns the id's of pinned shortcuts associated with the given package and user.
+     *
+     * If packageName is null, returns all pinned shortcuts regardless of package.
+     */
+    public List<ShortcutInfo> queryForPinnedShortcuts(String packageName, UserHandle user) {
+        return Collections.emptyList();
+    }
+
+    public List<ShortcutInfo> queryForPinnedShortcuts(String packageName,
+            List<String> shortcutIds, UserHandle user) {
+        return Collections.emptyList();
+    }
+
+    public List<ShortcutInfo> queryForAllShortcuts(UserHandle user) {
+        return Collections.emptyList();
+    }
+
+    public boolean hasHostPermission() {
+        return false;
+    }
+}
diff --git a/go/src_flags/com/android/launcher3/config/FeatureFlags.java b/go/src_flags/com/android/launcher3/config/FeatureFlags.java
deleted file mode 100644
index b11bb7c..0000000
--- a/go/src_flags/com/android/launcher3/config/FeatureFlags.java
+++ /dev/null
@@ -1,29 +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.config;
-
-/**
- * Defines a set of flags used to control various launcher behaviors
- */
-public final class FeatureFlags extends BaseFlags {
-
-    private FeatureFlags() {}
-
-    // Features to control Launcher3Go behavior
-    public static final boolean GO_DISABLE_WIDGETS = true;
-    public static final boolean LAUNCHER3_SPRING_ICONS = false;
-}
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..5b90f08
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,13 @@
+# Until all the dependencies move to android X
+android.useAndroidX = true
+android.enableJetifier = true
+
+ANDROID_X_VERSION=1.0.0-beta01
+
+GRADLE_CLASS_PATH=com.android.tools.build:gradle:3.3.0
+
+PROTOBUF_CLASS_PATH=com.google.protobuf:protobuf-gradle-plugin:0.8.6
+PROTOBUF_DEPENDENCY=com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-7
+
+BUILD_TOOLS_VERSION=28.0.3
+COMPILE_SDK=android-Q
\ No newline at end of file
diff --git a/iconloaderlib/Android.bp b/iconloaderlib/Android.bp
new file mode 100644
index 0000000..f12d16e
--- /dev/null
+++ b/iconloaderlib/Android.bp
@@ -0,0 +1,44 @@
+// 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.
+
+android_library {
+    name: "iconloader_base",
+    sdk_version: "28",
+    min_sdk_version: "21",
+    static_libs: [
+        "androidx.core_core",
+    ],
+    resource_dirs: [
+        "res",
+    ],
+    srcs: [
+        "src/**/*.java",
+    ],
+}
+
+android_library {
+    name: "iconloader",
+    sdk_version: "system_current",
+    min_sdk_version: "21",
+    static_libs: [
+        "androidx.core_core",
+    ],
+    resource_dirs: [
+        "res",
+    ],
+    srcs: [
+        "src/**/*.java",
+        "src_full_lib/**/*.java",
+    ],
+}
diff --git a/iconloaderlib/AndroidManifest.xml b/iconloaderlib/AndroidManifest.xml
new file mode 100644
index 0000000..b30258d
--- /dev/null
+++ b/iconloaderlib/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.launcher3.icons">
+</manifest>
diff --git a/iconloaderlib/build.gradle b/iconloaderlib/build.gradle
new file mode 100644
index 0000000..8a4d2b7
--- /dev/null
+++ b/iconloaderlib/build.gradle
@@ -0,0 +1,39 @@
+apply plugin: 'com.android.library'
+
+android {
+    compileSdkVersion COMPILE_SDK
+    buildToolsVersion BUILD_TOOLS_VERSION
+    publishNonDefault true
+
+    defaultConfig {
+        minSdkVersion 25
+        targetSdkVersion 28
+        versionCode 1
+        versionName "1.0"
+    }
+
+    sourceSets {
+        main {
+            java.srcDirs = ['src', 'src_full_lib']
+            manifest.srcFile 'AndroidManifest.xml'
+            res.srcDirs = ['res']
+        }
+    }
+
+    lintOptions {
+        abortOnError false
+    }
+
+    tasks.withType(JavaCompile) {
+        options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+}
+
+dependencies {
+    implementation "androidx.core:core:${ANDROID_X_VERSION}"
+}
diff --git a/iconloaderlib/res/drawable-v26/adaptive_icon_drawable_wrapper.xml b/iconloaderlib/res/drawable-v26/adaptive_icon_drawable_wrapper.xml
new file mode 100644
index 0000000..9f13cf5
--- /dev/null
+++ b/iconloaderlib/res/drawable-v26/adaptive_icon_drawable_wrapper.xml
@@ -0,0 +1,22 @@
+<?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/legacy_icon_background"/>
+    <foreground>
+        <com.android.launcher3.icons.FixedScaleDrawable />
+    </foreground>
+</adaptive-icon>
diff --git a/iconloaderlib/res/drawable/ic_instant_app_badge.xml b/iconloaderlib/res/drawable/ic_instant_app_badge.xml
new file mode 100644
index 0000000..b74317e
--- /dev/null
+++ b/iconloaderlib/res/drawable/ic_instant_app_badge.xml
@@ -0,0 +1,39 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="@dimen/profile_badge_size"
+    android:height="@dimen/profile_badge_size"
+    android:viewportWidth="18"
+    android:viewportHeight="18">
+
+    <path
+        android:fillColor="@android:color/black"
+        android:strokeWidth="1"
+        android:pathData="M 9 0 C 13.9705627485 0 18 4.02943725152 18 9 C 18 13.9705627485 13.9705627485 18 9 18 C 4.02943725152 18 0 13.9705627485 0 9 C 0 4.02943725152 4.02943725152 0 9 0 Z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:strokeWidth="1"
+        android:pathData="M 9 0 C 13.9705627485 0 18 4.02943725152 18 9 C 18 13.9705627485 13.9705627485 18 9 18 C 4.02943725152 18 0 13.9705627485 0 9 C 0 4.02943725152 4.02943725152 0 9 0 Z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:strokeWidth="1"
+        android:pathData="M 9 0 C 13.9705627485 0 18 4.02943725152 18 9 C 18 13.9705627485 13.9705627485 18 9 18 C 4.02943725152 18 0 13.9705627485 0 9 C 0 4.02943725152 4.02943725152 0 9 0 Z" />
+    <path
+        android:fillColor="@android:color/black"
+        android:fillAlpha="0.87"
+        android:strokeWidth="1"
+        android:pathData="M 6 10.4123279 L 8.63934949 10.4123279 L 8.63934949 15.6 L 12.5577168 7.84517705 L 9.94547194 7.84517705 L 9.94547194 2 Z" />
+</vector>
diff --git a/iconloaderlib/res/values/colors.xml b/iconloaderlib/res/values/colors.xml
new file mode 100644
index 0000000..873b2fc
--- /dev/null
+++ b/iconloaderlib/res/values/colors.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources>
+    <color name="legacy_icon_background">#FFFFFF</color>
+</resources>
diff --git a/iconloaderlib/res/values/config.xml b/iconloaderlib/res/values/config.xml
new file mode 100644
index 0000000..68c2d2e
--- /dev/null
+++ b/iconloaderlib/res/values/config.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources>
+
+    <!-- Various configurations to control the simple cache implementation -->
+
+    <dimen name="default_icon_bitmap_size">56dp</dimen>
+    <bool name="simple_cache_enable_im_memory">false</bool>
+    <string name="cache_db_name" translatable="false">app_icons.db</string>
+
+</resources>
\ No newline at end of file
diff --git a/iconloaderlib/res/values/dimens.xml b/iconloaderlib/res/values/dimens.xml
new file mode 100644
index 0000000..e8c0c44
--- /dev/null
+++ b/iconloaderlib/res/values/dimens.xml
@@ -0,0 +1,19 @@
+<?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>
+    <dimen name="profile_badge_size">24dp</dimen>
+</resources>
diff --git a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java
new file mode 100644
index 0000000..60320d6
--- /dev/null
+++ b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java
@@ -0,0 +1,351 @@
+package com.android.launcher3.icons;
+
+import static android.graphics.Paint.DITHER_FLAG;
+import static android.graphics.Paint.FILTER_BITMAP_FLAG;
+
+import static com.android.launcher3.icons.ShadowGenerator.BLUR_FACTOR;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PaintFlagsDrawFilter;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Process;
+import android.os.UserHandle;
+
+/**
+ * This class will be moved to androidx library. There shouldn't be any dependency outside
+ * this package.
+ */
+public class BaseIconFactory implements AutoCloseable {
+
+    private static final String TAG = "BaseIconFactory";
+    private static final int DEFAULT_WRAPPER_BACKGROUND = Color.WHITE;
+    static final boolean ATLEAST_OREO = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
+    static final boolean ATLEAST_P = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P;
+
+    private final Rect mOldBounds = new Rect();
+    protected final Context mContext;
+    private final Canvas mCanvas;
+    private final PackageManager mPm;
+    private final ColorExtractor mColorExtractor;
+    private boolean mDisableColorExtractor;
+
+    protected final int mFillResIconDpi;
+    protected final int mIconBitmapSize;
+
+    private IconNormalizer mNormalizer;
+    private ShadowGenerator mShadowGenerator;
+    private final boolean mShapeDetection;
+
+    private Drawable mWrapperIcon;
+    private int mWrapperBackgroundColor = DEFAULT_WRAPPER_BACKGROUND;
+
+    protected BaseIconFactory(Context context, int fillResIconDpi, int iconBitmapSize,
+            boolean shapeDetection) {
+        mContext = context.getApplicationContext();
+        mShapeDetection = shapeDetection;
+        mFillResIconDpi = fillResIconDpi;
+        mIconBitmapSize = iconBitmapSize;
+
+        mPm = mContext.getPackageManager();
+        mColorExtractor = new ColorExtractor();
+
+        mCanvas = new Canvas();
+        mCanvas.setDrawFilter(new PaintFlagsDrawFilter(DITHER_FLAG, FILTER_BITMAP_FLAG));
+        clear();
+    }
+
+    protected BaseIconFactory(Context context, int fillResIconDpi, int iconBitmapSize) {
+        this(context, fillResIconDpi, iconBitmapSize, false);
+    }
+
+    protected void clear() {
+        mWrapperBackgroundColor = DEFAULT_WRAPPER_BACKGROUND;
+        mDisableColorExtractor = false;
+    }
+
+    public ShadowGenerator getShadowGenerator() {
+        if (mShadowGenerator == null) {
+            mShadowGenerator = new ShadowGenerator(mIconBitmapSize);
+        }
+        return mShadowGenerator;
+    }
+
+    public IconNormalizer getNormalizer() {
+        if (mNormalizer == null) {
+            mNormalizer = new IconNormalizer(mContext, mIconBitmapSize, mShapeDetection);
+        }
+        return mNormalizer;
+    }
+
+    @SuppressWarnings("deprecation")
+    public BitmapInfo createIconBitmap(Intent.ShortcutIconResource iconRes) {
+        try {
+            Resources resources = mPm.getResourcesForApplication(iconRes.packageName);
+            if (resources != null) {
+                final int id = resources.getIdentifier(iconRes.resourceName, null, null);
+                // do not stamp old legacy shortcuts as the app may have already forgotten about it
+                return createBadgedIconBitmap(
+                        resources.getDrawableForDensity(id, mFillResIconDpi),
+                        Process.myUserHandle() /* only available on primary user */,
+                        false /* do not apply legacy treatment */);
+            }
+        } catch (Exception e) {
+            // Icon not found.
+        }
+        return null;
+    }
+
+    public BitmapInfo createIconBitmap(Bitmap icon) {
+        if (mIconBitmapSize != icon.getWidth() || mIconBitmapSize != icon.getHeight()) {
+            icon = createIconBitmap(new BitmapDrawable(mContext.getResources(), icon), 1f);
+        }
+
+        return BitmapInfo.fromBitmap(icon, mDisableColorExtractor ? null : mColorExtractor);
+    }
+
+    public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user,
+            boolean shrinkNonAdaptiveIcons) {
+        return createBadgedIconBitmap(icon, user, shrinkNonAdaptiveIcons, false, null);
+    }
+
+    public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user,
+            int iconAppTargetSdk) {
+        return createBadgedIconBitmap(icon, user, iconAppTargetSdk, false);
+    }
+
+    public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user,
+            int iconAppTargetSdk, boolean isInstantApp) {
+        return createBadgedIconBitmap(icon, user, iconAppTargetSdk, isInstantApp, null);
+    }
+
+    public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user,
+            int iconAppTargetSdk, boolean isInstantApp, float[] scale) {
+        boolean shrinkNonAdaptiveIcons = ATLEAST_P ||
+                (ATLEAST_OREO && iconAppTargetSdk >= Build.VERSION_CODES.O);
+        return createBadgedIconBitmap(icon, user, shrinkNonAdaptiveIcons, isInstantApp, scale);
+    }
+
+    public Bitmap createScaledBitmapWithoutShadow(Drawable icon, int iconAppTargetSdk) {
+        boolean shrinkNonAdaptiveIcons = ATLEAST_P ||
+                (ATLEAST_OREO && iconAppTargetSdk >= Build.VERSION_CODES.O);
+        return  createScaledBitmapWithoutShadow(icon, shrinkNonAdaptiveIcons);
+    }
+
+    /**
+     * Creates bitmap using the source drawable and various parameters.
+     * The bitmap is visually normalized with other icons and has enough spacing to add shadow.
+     *
+     * @param icon                      source of the icon
+     * @param user                      info can be used for a badge
+     * @param shrinkNonAdaptiveIcons    {@code true} if non adaptive icons should be treated
+     * @param isInstantApp              info can be used for a badge
+     * @param scale                     returns the scale result from normalization
+     * @return a bitmap suitable for disaplaying as an icon at various system UIs.
+     */
+    public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user,
+            boolean shrinkNonAdaptiveIcons, boolean isInstantApp, float[] scale) {
+        if (scale == null) {
+            scale = new float[1];
+        }
+        icon = normalizeAndWrapToAdaptiveIcon(icon, shrinkNonAdaptiveIcons, null, scale);
+        Bitmap bitmap = createIconBitmap(icon, scale[0]);
+        if (ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) {
+            mCanvas.setBitmap(bitmap);
+            getShadowGenerator().recreateIcon(Bitmap.createBitmap(bitmap), mCanvas);
+            mCanvas.setBitmap(null);
+        }
+
+        if (isInstantApp) {
+            badgeWithDrawable(bitmap, mContext.getDrawable(R.drawable.ic_instant_app_badge));
+        }
+        if (user != null) {
+            BitmapDrawable drawable = new FixedSizeBitmapDrawable(bitmap);
+            Drawable badged = mPm.getUserBadgedIcon(drawable, user);
+            if (badged instanceof BitmapDrawable) {
+                bitmap = ((BitmapDrawable) badged).getBitmap();
+            } else {
+                bitmap = createIconBitmap(badged, 1f);
+            }
+        }
+        return BitmapInfo.fromBitmap(bitmap, mDisableColorExtractor ? null : mColorExtractor);
+    }
+
+    public Bitmap createScaledBitmapWithoutShadow(Drawable icon, boolean shrinkNonAdaptiveIcons) {
+        RectF iconBounds = new RectF();
+        float[] scale = new float[1];
+        icon = normalizeAndWrapToAdaptiveIcon(icon, shrinkNonAdaptiveIcons, iconBounds, scale);
+        return createIconBitmap(icon,
+                Math.min(scale[0], ShadowGenerator.getScaleForBounds(iconBounds)));
+    }
+
+    /**
+     * Sets the background color used for wrapped adaptive icon
+     */
+    public void setWrapperBackgroundColor(int color) {
+        mWrapperBackgroundColor = (Color.alpha(color) < 255) ? DEFAULT_WRAPPER_BACKGROUND : color;
+    }
+
+    /**
+     * Disables the dominant color extraction for all icons loaded.
+     */
+    public void disableColorExtraction() {
+        mDisableColorExtractor = true;
+    }
+
+    private Drawable normalizeAndWrapToAdaptiveIcon(Drawable icon, boolean shrinkNonAdaptiveIcons,
+            RectF outIconBounds, float[] outScale) {
+        float scale = 1f;
+
+        if (shrinkNonAdaptiveIcons && ATLEAST_OREO) {
+            if (mWrapperIcon == null) {
+                mWrapperIcon = mContext.getDrawable(R.drawable.adaptive_icon_drawable_wrapper)
+                        .mutate();
+            }
+            AdaptiveIconDrawable dr = (AdaptiveIconDrawable) mWrapperIcon;
+            dr.setBounds(0, 0, 1, 1);
+            boolean[] outShape = new boolean[1];
+            scale = getNormalizer().getScale(icon, outIconBounds, dr.getIconMask(), outShape);
+            if (!(icon instanceof AdaptiveIconDrawable) && !outShape[0]) {
+                FixedScaleDrawable fsd = ((FixedScaleDrawable) dr.getForeground());
+                fsd.setDrawable(icon);
+                fsd.setScale(scale);
+                icon = dr;
+                scale = getNormalizer().getScale(icon, outIconBounds, null, null);
+
+                ((ColorDrawable) dr.getBackground()).setColor(mWrapperBackgroundColor);
+            }
+        } else {
+            scale = getNormalizer().getScale(icon, outIconBounds, null, null);
+        }
+
+        outScale[0] = scale;
+        return icon;
+    }
+
+    /**
+     * Adds the {@param badge} on top of {@param target} using the badge dimensions.
+     */
+    public void badgeWithDrawable(Bitmap target, Drawable badge) {
+        mCanvas.setBitmap(target);
+        badgeWithDrawable(mCanvas, badge);
+        mCanvas.setBitmap(null);
+    }
+
+    /**
+     * Adds the {@param badge} on top of {@param target} using the badge dimensions.
+     */
+    public void badgeWithDrawable(Canvas target, Drawable badge) {
+        int badgeSize = mContext.getResources().getDimensionPixelSize(R.dimen.profile_badge_size);
+        badge.setBounds(mIconBitmapSize - badgeSize, mIconBitmapSize - badgeSize,
+                mIconBitmapSize, mIconBitmapSize);
+        badge.draw(target);
+    }
+
+    private Bitmap createIconBitmap(Drawable icon, float scale) {
+        return createIconBitmap(icon, scale, mIconBitmapSize);
+    }
+
+    /**
+     * @param icon drawable that should be flattened to a bitmap
+     * @param scale the scale to apply before drawing {@param icon} on the canvas
+     */
+    public Bitmap createIconBitmap(Drawable icon, float scale, int size) {
+        Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+        if (icon == null) {
+            return bitmap;
+        }
+        mCanvas.setBitmap(bitmap);
+        mOldBounds.set(icon.getBounds());
+
+        if (ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) {
+            int offset = Math.max((int) Math.ceil(BLUR_FACTOR * size),
+                    Math.round(size * (1 - scale) / 2 ));
+            icon.setBounds(offset, offset, size - offset, size - offset);
+            icon.draw(mCanvas);
+        } else {
+            if (icon instanceof BitmapDrawable) {
+                BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;
+                Bitmap b = bitmapDrawable.getBitmap();
+                if (bitmap != null && b.getDensity() == Bitmap.DENSITY_NONE) {
+                    bitmapDrawable.setTargetDensity(mContext.getResources().getDisplayMetrics());
+                }
+            }
+            int width = size;
+            int height = size;
+
+            int intrinsicWidth = icon.getIntrinsicWidth();
+            int intrinsicHeight = icon.getIntrinsicHeight();
+            if (intrinsicWidth > 0 && intrinsicHeight > 0) {
+                // Scale the icon proportionally to the icon dimensions
+                final float ratio = (float) intrinsicWidth / intrinsicHeight;
+                if (intrinsicWidth > intrinsicHeight) {
+                    height = (int) (width / ratio);
+                } else if (intrinsicHeight > intrinsicWidth) {
+                    width = (int) (height * ratio);
+                }
+            }
+            final int left = (size - width) / 2;
+            final int top = (size - height) / 2;
+            icon.setBounds(left, top, left + width, top + height);
+            mCanvas.save();
+            mCanvas.scale(scale, scale, size / 2, size / 2);
+            icon.draw(mCanvas);
+            mCanvas.restore();
+
+        }
+        icon.setBounds(mOldBounds);
+        mCanvas.setBitmap(null);
+        return bitmap;
+    }
+
+    @Override
+    public void close() {
+        clear();
+    }
+
+    public BitmapInfo makeDefaultIcon(UserHandle user) {
+        return createBadgedIconBitmap(getFullResDefaultActivityIcon(mFillResIconDpi),
+                user, Build.VERSION.SDK_INT);
+    }
+
+    public static Drawable getFullResDefaultActivityIcon(int iconDpi) {
+        return Resources.getSystem().getDrawableForDensity(
+                Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
+                        ? android.R.drawable.sym_def_app_icon : android.R.mipmap.sym_def_app_icon,
+                iconDpi);
+    }
+
+    /**
+     * An extension of {@link BitmapDrawable} which returns the bitmap pixel size as intrinsic size.
+     * This allows the badging to be done based on the action bitmap size rather than
+     * the scaled bitmap size.
+     */
+    private static class FixedSizeBitmapDrawable extends BitmapDrawable {
+
+        public FixedSizeBitmapDrawable(Bitmap bitmap) {
+            super(null, bitmap);
+        }
+
+        @Override
+        public int getIntrinsicHeight() {
+            return getBitmap().getWidth();
+        }
+
+        @Override
+        public int getIntrinsicWidth() {
+            return getBitmap().getWidth();
+        }
+    }
+}
diff --git a/iconloaderlib/src/com/android/launcher3/icons/BitmapInfo.java b/iconloaderlib/src/com/android/launcher3/icons/BitmapInfo.java
new file mode 100644
index 0000000..245561e
--- /dev/null
+++ b/iconloaderlib/src/com/android/launcher3/icons/BitmapInfo.java
@@ -0,0 +1,49 @@
+/*
+ * 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.icons;
+
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+
+public class BitmapInfo {
+
+    public static final Bitmap LOW_RES_ICON = Bitmap.createBitmap(1, 1, Config.ALPHA_8);
+
+    public Bitmap icon;
+    public int color;
+
+    public void applyTo(BitmapInfo info) {
+        info.icon = icon;
+        info.color = color;
+    }
+
+    public final boolean isLowRes() {
+        return LOW_RES_ICON == icon;
+    }
+
+    public static BitmapInfo fromBitmap(Bitmap bitmap) {
+        return fromBitmap(bitmap, null);
+    }
+
+    public static BitmapInfo fromBitmap(Bitmap bitmap, ColorExtractor dominantColorExtractor) {
+        BitmapInfo info = new BitmapInfo();
+        info.icon = bitmap;
+        info.color = dominantColorExtractor != null
+                ? dominantColorExtractor.findDominantColorByHue(bitmap)
+                : 0;
+        return info;
+    }
+}
diff --git a/iconloaderlib/src/com/android/launcher3/icons/BitmapRenderer.java b/iconloaderlib/src/com/android/launcher3/icons/BitmapRenderer.java
new file mode 100644
index 0000000..a66b929
--- /dev/null
+++ b/iconloaderlib/src/com/android/launcher3/icons/BitmapRenderer.java
@@ -0,0 +1,50 @@
+/*
+ * 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.icons;
+
+import android.annotation.TargetApi;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Picture;
+import android.os.Build;
+
+/**
+ * Interface representing a bitmap draw operation.
+ */
+public interface BitmapRenderer {
+
+    boolean USE_HARDWARE_BITMAP = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P;
+
+    static Bitmap createSoftwareBitmap(int width, int height, BitmapRenderer renderer) {
+        Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        renderer.draw(new Canvas(result));
+        return result;
+    }
+
+    @TargetApi(Build.VERSION_CODES.P)
+    static Bitmap createHardwareBitmap(int width, int height, BitmapRenderer renderer) {
+        if (!USE_HARDWARE_BITMAP) {
+            return createSoftwareBitmap(width, height, renderer);
+        }
+
+        Picture picture = new Picture();
+        renderer.draw(picture.beginRecording(width, height));
+        picture.endRecording();
+        return Bitmap.createBitmap(picture);
+    }
+
+    void draw(Canvas out);
+}
diff --git a/iconloaderlib/src/com/android/launcher3/icons/ColorExtractor.java b/iconloaderlib/src/com/android/launcher3/icons/ColorExtractor.java
new file mode 100644
index 0000000..87bda82
--- /dev/null
+++ b/iconloaderlib/src/com/android/launcher3/icons/ColorExtractor.java
@@ -0,0 +1,127 @@
+/*
+ * 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.icons;
+
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.util.SparseArray;
+import java.util.Arrays;
+
+/**
+ * Utility class for extracting colors from a bitmap.
+ */
+public class ColorExtractor {
+
+    private final int NUM_SAMPLES = 20;
+    private final float[] mTmpHsv = new float[3];
+    private final float[] mTmpHueScoreHistogram = new float[360];
+    private final int[] mTmpPixels = new int[NUM_SAMPLES];
+    private final SparseArray<Float> mTmpRgbScores = new SparseArray<>();
+
+    /**
+     * This picks a dominant color, looking for high-saturation, high-value, repeated hues.
+     * @param bitmap The bitmap to scan
+     */
+    public int findDominantColorByHue(Bitmap bitmap) {
+        return findDominantColorByHue(bitmap, NUM_SAMPLES);
+    }
+
+    /**
+     * This picks a dominant color, looking for high-saturation, high-value, repeated hues.
+     * @param bitmap The bitmap to scan
+     */
+    public int findDominantColorByHue(Bitmap bitmap, int samples) {
+        final int height = bitmap.getHeight();
+        final int width = bitmap.getWidth();
+        int sampleStride = (int) Math.sqrt((height * width) / samples);
+        if (sampleStride < 1) {
+            sampleStride = 1;
+        }
+
+        // This is an out-param, for getting the hsv values for an rgb
+        float[] hsv = mTmpHsv;
+        Arrays.fill(hsv, 0);
+
+        // First get the best hue, by creating a histogram over 360 hue buckets,
+        // where each pixel contributes a score weighted by saturation, value, and alpha.
+        float[] hueScoreHistogram = mTmpHueScoreHistogram;
+        Arrays.fill(hueScoreHistogram, 0);
+        float highScore = -1;
+        int bestHue = -1;
+
+        int[] pixels = mTmpPixels;
+        Arrays.fill(pixels, 0);
+        int pixelCount = 0;
+
+        for (int y = 0; y < height; y += sampleStride) {
+            for (int x = 0; x < width; x += sampleStride) {
+                int argb = bitmap.getPixel(x, y);
+                int alpha = 0xFF & (argb >> 24);
+                if (alpha < 0x80) {
+                    // Drop mostly-transparent pixels.
+                    continue;
+                }
+                // Remove the alpha channel.
+                int rgb = argb | 0xFF000000;
+                Color.colorToHSV(rgb, hsv);
+                // Bucket colors by the 360 integer hues.
+                int hue = (int) hsv[0];
+                if (hue < 0 || hue >= hueScoreHistogram.length) {
+                    // Defensively avoid array bounds violations.
+                    continue;
+                }
+                if (pixelCount < samples) {
+                    pixels[pixelCount++] = rgb;
+                }
+                float score = hsv[1] * hsv[2];
+                hueScoreHistogram[hue] += score;
+                if (hueScoreHistogram[hue] > highScore) {
+                    highScore = hueScoreHistogram[hue];
+                    bestHue = hue;
+                }
+            }
+        }
+
+        SparseArray<Float> rgbScores = mTmpRgbScores;
+        rgbScores.clear();
+        int bestColor = 0xff000000;
+        highScore = -1;
+        // Go back over the RGB colors that match the winning hue,
+        // creating a histogram of weighted s*v scores, for up to 100*100 [s,v] buckets.
+        // The highest-scoring RGB color wins.
+        for (int i = 0; i < pixelCount; i++) {
+            int rgb = pixels[i];
+            Color.colorToHSV(rgb, hsv);
+            int hue = (int) hsv[0];
+            if (hue == bestHue) {
+                float s = hsv[1];
+                float v = hsv[2];
+                int bucket = (int) (s * 100) + (int) (v * 10000);
+                // Score by cumulative saturation * value.
+                float score = s * v;
+                Float oldTotal = rgbScores.get(bucket);
+                float newTotal = oldTotal == null ? score : oldTotal + score;
+                rgbScores.put(bucket, newTotal);
+                if (newTotal > highScore) {
+                    highScore = newTotal;
+                    // All the colors in the winning bucket are very similar. Last in wins.
+                    bestColor = rgb;
+                }
+            }
+        }
+        return bestColor;
+    }
+}
diff --git a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java
new file mode 100644
index 0000000..97a0fd3
--- /dev/null
+++ b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java
@@ -0,0 +1,143 @@
+/*
+ * 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.icons;
+
+import static android.graphics.Paint.ANTI_ALIAS_FLAG;
+import static android.graphics.Paint.FILTER_BITMAP_FLAG;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PathMeasure;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.util.Log;
+import android.view.ViewDebug;
+
+/**
+ * Used to draw a notification dot on top of an icon.
+ */
+public class DotRenderer {
+
+    private static final String TAG = "DotRenderer";
+
+    // The dot size is defined as a percentage of the app icon size.
+    private static final float SIZE_PERCENTAGE = 0.228f;
+
+    private final float mCircleRadius;
+    private final Paint mCirclePaint = new Paint(ANTI_ALIAS_FLAG | FILTER_BITMAP_FLAG);
+
+    private final Bitmap mBackgroundWithShadow;
+    private final float mBitmapOffset;
+
+    // Stores the center x and y position as a percentage (0 to 1) of the icon size
+    private final float[] mRightDotPosition;
+    private final float[] mLeftDotPosition;
+
+    public DotRenderer(int iconSizePx, Path iconShapePath, int pathSize) {
+        int size = Math.round(SIZE_PERCENTAGE * iconSizePx);
+        ShadowGenerator.Builder builder = new ShadowGenerator.Builder(Color.TRANSPARENT);
+        builder.ambientShadowAlpha = 88;
+        mBackgroundWithShadow = builder.setupBlurForSize(size).createPill(size, size);
+        mCircleRadius = builder.radius;
+
+        mBitmapOffset = -mBackgroundWithShadow.getHeight() * 0.5f; // Same as width.
+
+        // Find the points on the path that are closest to the top left and right corners.
+        mLeftDotPosition = getPathPoint(iconShapePath, pathSize, -1);
+        mRightDotPosition = getPathPoint(iconShapePath, pathSize, 1);
+    }
+
+    private static float[] getPathPoint(Path path, float size, float direction) {
+        float halfSize = size / 2;
+        // Small delta so that we don't get a zero size triangle
+        float delta = 1;
+
+        float x = halfSize + direction * halfSize;
+        Path trianglePath = new Path();
+        trianglePath.moveTo(halfSize, halfSize);
+        trianglePath.lineTo(x + delta * direction, 0);
+        trianglePath.lineTo(x, -delta);
+        trianglePath.close();
+
+        trianglePath.op(path, Path.Op.INTERSECT);
+        float[] pos = new float[2];
+        new PathMeasure(trianglePath, false).getPosTan(0, pos, null);
+
+        pos[0] = pos[0] / size;
+        pos[1] = pos[1] / size;
+        return pos;
+    }
+
+    public float[] getLeftDotPosition() {
+        return mLeftDotPosition;
+    }
+
+    public float[] getRightDotPosition() {
+        return mRightDotPosition;
+    }
+
+    /**
+     * Draw a circle on top of the canvas according to the given params.
+     */
+    public void draw(Canvas canvas, DrawParams params) {
+        if (params == null) {
+            Log.e(TAG, "Invalid null argument(s) passed in call to draw.");
+            return;
+        }
+        canvas.save();
+
+        Rect iconBounds = params.iconBounds;
+        float[] dotPosition = params.leftAlign ? mLeftDotPosition : mRightDotPosition;
+        float dotCenterX = iconBounds.left + iconBounds.width() * dotPosition[0];
+        float dotCenterY = iconBounds.top + iconBounds.height() * dotPosition[1];
+
+        // Ensure dot fits entirely in canvas clip bounds.
+        Rect canvasBounds = canvas.getClipBounds();
+        float offsetX = params.leftAlign
+                ? Math.max(0, canvasBounds.left - (dotCenterX + mBitmapOffset))
+                : Math.min(0, canvasBounds.right - (dotCenterX - mBitmapOffset));
+        float offsetY = Math.max(0, canvasBounds.top - (dotCenterY + mBitmapOffset));
+
+        // We draw the dot relative to its center.
+        canvas.translate(dotCenterX + offsetX, dotCenterY + offsetY);
+        canvas.scale(params.scale, params.scale);
+
+        mCirclePaint.setColor(Color.BLACK);
+        canvas.drawBitmap(mBackgroundWithShadow, mBitmapOffset, mBitmapOffset, mCirclePaint);
+        mCirclePaint.setColor(params.color);
+        canvas.drawCircle(0, 0, mCircleRadius, mCirclePaint);
+        canvas.restore();
+    }
+
+    public static class DrawParams {
+        /** The color (possibly based on the icon) to use for the dot. */
+        @ViewDebug.ExportedProperty(category = "notification dot", formatToHexString = true)
+        public int color;
+        /** The bounds of the icon that the dot is drawn on top of. */
+        @ViewDebug.ExportedProperty(category = "notification dot")
+        public Rect iconBounds = new Rect();
+        /** The progress of the animation, from 0 to 1. */
+        @ViewDebug.ExportedProperty(category = "notification dot")
+        public float scale;
+        /** Whether the dot should align to the top left of the icon rather than the top right. */
+        @ViewDebug.ExportedProperty(category = "notification dot")
+        public boolean leftAlign;
+    }
+}
diff --git a/iconloaderlib/src/com/android/launcher3/icons/FixedScaleDrawable.java b/iconloaderlib/src/com/android/launcher3/icons/FixedScaleDrawable.java
new file mode 100644
index 0000000..516965e
--- /dev/null
+++ b/iconloaderlib/src/com/android/launcher3/icons/FixedScaleDrawable.java
@@ -0,0 +1,53 @@
+package com.android.launcher3.icons;
+
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
+import android.graphics.Canvas;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.DrawableWrapper;
+import android.util.AttributeSet;
+
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * Extension of {@link DrawableWrapper} which scales the child drawables by a fixed amount.
+ */
+public class FixedScaleDrawable extends DrawableWrapper {
+
+    // TODO b/33553066 use the constant defined in MaskableIconDrawable
+    private static final float LEGACY_ICON_SCALE = .7f * .6667f;
+    private float mScaleX, mScaleY;
+
+    public FixedScaleDrawable() {
+        super(new ColorDrawable());
+        mScaleX = LEGACY_ICON_SCALE;
+        mScaleY = LEGACY_ICON_SCALE;
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        int saveCount = canvas.save();
+        canvas.scale(mScaleX, mScaleY,
+                getBounds().exactCenterX(), getBounds().exactCenterY());
+        super.draw(canvas);
+        canvas.restoreToCount(saveCount);
+    }
+
+    @Override
+    public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs) { }
+
+    @Override
+    public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) { }
+
+    public void setScale(float scale) {
+        float h = getIntrinsicHeight();
+        float w = getIntrinsicWidth();
+        mScaleX = scale * LEGACY_ICON_SCALE;
+        mScaleY = scale * LEGACY_ICON_SCALE;
+        if (h > w && w > 0) {
+            mScaleX *= w / h;
+        } else if (w > h && h > 0) {
+            mScaleY *= h / w;
+        }
+    }
+}
diff --git a/iconloaderlib/src/com/android/launcher3/icons/GraphicsUtils.java b/iconloaderlib/src/com/android/launcher3/icons/GraphicsUtils.java
new file mode 100644
index 0000000..3e818a5
--- /dev/null
+++ b/iconloaderlib/src/com/android/launcher3/icons/GraphicsUtils.java
@@ -0,0 +1,76 @@
+/*
+ * 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.icons;
+
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.RegionIterator;
+import android.util.Log;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import androidx.annotation.ColorInt;
+
+public class GraphicsUtils {
+
+    private static final String TAG = "GraphicsUtils";
+
+    /**
+     * Set the alpha component of {@code color} to be {@code alpha}. Unlike the support lib version,
+     * it bounds the alpha in valid range instead of throwing an exception to allow for safer
+     * interpolation of color animations
+     */
+    @ColorInt
+    public static int setColorAlphaBound(int color, int alpha) {
+        if (alpha < 0) {
+            alpha = 0;
+        } else if (alpha > 255) {
+            alpha = 255;
+        }
+        return (color & 0x00ffffff) | (alpha << 24);
+    }
+
+    /**
+     * Compresses the bitmap to a byte array for serialization.
+     */
+    public static byte[] flattenBitmap(Bitmap bitmap) {
+        // Try go guesstimate how much space the icon will take when serialized
+        // to avoid unnecessary allocations/copies during the write (4 bytes per pixel).
+        int size = bitmap.getWidth() * bitmap.getHeight() * 4;
+        ByteArrayOutputStream out = new ByteArrayOutputStream(size);
+        try {
+            bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
+            out.flush();
+            out.close();
+            return out.toByteArray();
+        } catch (IOException e) {
+            Log.w(TAG, "Could not write bitmap");
+            return null;
+        }
+    }
+
+    public static int getArea(Region r) {
+        RegionIterator itr = new RegionIterator(r);
+        int area = 0;
+        Rect tempRect = new Rect();
+        while (itr.next(tempRect)) {
+            area += tempRect.width() * tempRect.height();
+        }
+        return area;
+    }
+}
diff --git a/iconloaderlib/src/com/android/launcher3/icons/IconNormalizer.java b/iconloaderlib/src/com/android/launcher3/icons/IconNormalizer.java
new file mode 100644
index 0000000..de39e79
--- /dev/null
+++ b/iconloaderlib/src/com/android/launcher3/icons/IconNormalizer.java
@@ -0,0 +1,411 @@
+/*
+ * 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.icons;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Region;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.util.Log;
+
+import java.nio.ByteBuffer;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+public class IconNormalizer {
+
+    private static final String TAG = "IconNormalizer";
+    private static final boolean DEBUG = false;
+    // Ratio of icon visible area to full icon size for a square shaped icon
+    private static final float MAX_SQUARE_AREA_FACTOR = 375.0f / 576;
+    // Ratio of icon visible area to full icon size for a circular shaped icon
+    private static final float MAX_CIRCLE_AREA_FACTOR = 380.0f / 576;
+
+    private static final float CIRCLE_AREA_BY_RECT = (float) Math.PI / 4;
+
+    // Slope used to calculate icon visible area to full icon size for any generic shaped icon.
+    private static final float LINEAR_SCALE_SLOPE =
+            (MAX_CIRCLE_AREA_FACTOR - MAX_SQUARE_AREA_FACTOR) / (1 - CIRCLE_AREA_BY_RECT);
+
+    private static final int MIN_VISIBLE_ALPHA = 40;
+
+    // Shape detection related constants
+    private static final float BOUND_RATIO_MARGIN = .05f;
+    private static final float PIXEL_DIFF_PERCENTAGE_THRESHOLD = 0.005f;
+    private static final float SCALE_NOT_INITIALIZED = 0;
+
+    // Ratio of the diameter of an normalized circular icon to the actual icon size.
+    public static final float ICON_VISIBLE_AREA_FACTOR = 0.92f;
+
+    private final int mMaxSize;
+    private final Bitmap mBitmap;
+    private final Canvas mCanvas;
+    private final Paint mPaintMaskShape;
+    private final Paint mPaintMaskShapeOutline;
+    private final byte[] mPixels;
+
+    private final RectF mAdaptiveIconBounds;
+    private float mAdaptiveIconScale;
+
+    private boolean mEnableShapeDetection;
+
+    // for each y, stores the position of the leftmost x and the rightmost x
+    private final float[] mLeftBorder;
+    private final float[] mRightBorder;
+    private final Rect mBounds;
+    private final Path mShapePath;
+    private final Matrix mMatrix;
+
+    /** package private **/
+    IconNormalizer(Context context, int iconBitmapSize, boolean shapeDetection) {
+        // Use twice the icon size as maximum size to avoid scaling down twice.
+        mMaxSize = iconBitmapSize * 2;
+        mBitmap = Bitmap.createBitmap(mMaxSize, mMaxSize, Bitmap.Config.ALPHA_8);
+        mCanvas = new Canvas(mBitmap);
+        mPixels = new byte[mMaxSize * mMaxSize];
+        mLeftBorder = new float[mMaxSize];
+        mRightBorder = new float[mMaxSize];
+        mBounds = new Rect();
+        mAdaptiveIconBounds = new RectF();
+
+        mPaintMaskShape = new Paint();
+        mPaintMaskShape.setColor(Color.RED);
+        mPaintMaskShape.setStyle(Paint.Style.FILL);
+        mPaintMaskShape.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
+
+        mPaintMaskShapeOutline = new Paint();
+        mPaintMaskShapeOutline.setStrokeWidth(
+                2 * context.getResources().getDisplayMetrics().density);
+        mPaintMaskShapeOutline.setStyle(Paint.Style.STROKE);
+        mPaintMaskShapeOutline.setColor(Color.BLACK);
+        mPaintMaskShapeOutline.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
+
+        mShapePath = new Path();
+        mMatrix = new Matrix();
+        mAdaptiveIconScale = SCALE_NOT_INITIALIZED;
+        mEnableShapeDetection = shapeDetection;
+    }
+
+    private static float getScale(float hullArea, float boundingArea, float fullArea) {
+        float hullByRect = hullArea / boundingArea;
+        float scaleRequired;
+        if (hullByRect < CIRCLE_AREA_BY_RECT) {
+            scaleRequired = MAX_CIRCLE_AREA_FACTOR;
+        } else {
+            scaleRequired = MAX_SQUARE_AREA_FACTOR + LINEAR_SCALE_SLOPE * (1 - hullByRect);
+        }
+
+        float areaScale = hullArea / fullArea;
+        // Use sqrt of the final ratio as the images is scaled across both width and height.
+        return areaScale > scaleRequired ? (float) Math.sqrt(scaleRequired / areaScale) : 1;
+    }
+
+    /**
+     * @param d Should be AdaptiveIconDrawable
+     * @param size Canvas size to use
+     */
+    @TargetApi(Build.VERSION_CODES.O)
+    public static float normalizeAdaptiveIcon(Drawable d, int size, @Nullable RectF outBounds) {
+        Rect tmpBounds = new Rect(d.getBounds());
+        d.setBounds(0, 0, size, size);
+
+        Path path = ((AdaptiveIconDrawable) d).getIconMask();
+        Region region = new Region();
+        region.setPath(path, new Region(0, 0, size, size));
+
+        Rect hullBounds = region.getBounds();
+        int hullArea = GraphicsUtils.getArea(region);
+
+        if (outBounds != null) {
+            float sizeF = size;
+            outBounds.set(
+                    hullBounds.left / sizeF,
+                    hullBounds.top / sizeF,
+                    1 - (hullBounds.right / sizeF),
+                    1 - (hullBounds.bottom / sizeF));
+        }
+        d.setBounds(tmpBounds);
+        return getScale(hullArea, hullArea, size * size);
+    }
+
+    /**
+     * Returns if the shape of the icon is same as the path.
+     * For this method to work, the shape path bounds should be in [0,1]x[0,1] bounds.
+     */
+    private boolean isShape(Path maskPath) {
+        // Condition1:
+        // If width and height of the path not close to a square, then the icon shape is
+        // not same as the mask shape.
+        float iconRatio = ((float) mBounds.width()) / mBounds.height();
+        if (Math.abs(iconRatio - 1) > BOUND_RATIO_MARGIN) {
+            if (DEBUG) {
+                Log.d(TAG, "Not same as mask shape because width != height. " + iconRatio);
+            }
+            return false;
+        }
+
+        // Condition 2:
+        // Actual icon (white) and the fitted shape (e.g., circle)(red) XOR operation
+        // should generate transparent image, if the actual icon is equivalent to the shape.
+
+        // Fit the shape within the icon's bounding box
+        mMatrix.reset();
+        mMatrix.setScale(mBounds.width(), mBounds.height());
+        mMatrix.postTranslate(mBounds.left, mBounds.top);
+        maskPath.transform(mMatrix, mShapePath);
+
+        // XOR operation
+        mCanvas.drawPath(mShapePath, mPaintMaskShape);
+
+        // DST_OUT operation around the mask path outline
+        mCanvas.drawPath(mShapePath, mPaintMaskShapeOutline);
+
+        // Check if the result is almost transparent
+        return isTransparentBitmap();
+    }
+
+    /**
+     * Used to determine if certain the bitmap is transparent.
+     */
+    private boolean isTransparentBitmap() {
+        ByteBuffer buffer = ByteBuffer.wrap(mPixels);
+        buffer.rewind();
+        mBitmap.copyPixelsToBuffer(buffer);
+
+        int y = mBounds.top;
+        // buffer position
+        int index = y * mMaxSize;
+        // buffer shift after every row, width of buffer = mMaxSize
+        int rowSizeDiff = mMaxSize - mBounds.right;
+
+        int sum = 0;
+        for (; y < mBounds.bottom; y++) {
+            index += mBounds.left;
+            for (int x = mBounds.left; x < mBounds.right; x++) {
+                if ((mPixels[index] & 0xFF) > MIN_VISIBLE_ALPHA) {
+                    sum++;
+                }
+                index++;
+            }
+            index += rowSizeDiff;
+        }
+
+        float percentageDiffPixels = ((float) sum) / (mBounds.width() * mBounds.height());
+        return percentageDiffPixels < PIXEL_DIFF_PERCENTAGE_THRESHOLD;
+    }
+
+    /**
+     * Returns the amount by which the {@param d} should be scaled (in both dimensions) so that it
+     * matches the design guidelines for a launcher icon.
+     *
+     * We first calculate the convex hull of the visible portion of the icon.
+     * This hull then compared with the bounding rectangle of the hull to find how closely it
+     * resembles a circle and a square, by comparing the ratio of the areas. Note that this is not an
+     * ideal solution but it gives satisfactory result without affecting the performance.
+     *
+     * This closeness is used to determine the ratio of hull area to the full icon size.
+     * Refer {@link #MAX_CIRCLE_AREA_FACTOR} and {@link #MAX_SQUARE_AREA_FACTOR}
+     *
+     * @param outBounds optional rect to receive the fraction distance from each edge.
+     */
+    public synchronized float getScale(@NonNull Drawable d, @Nullable RectF outBounds,
+            @Nullable Path path, @Nullable boolean[] outMaskShape) {
+        if (BaseIconFactory.ATLEAST_OREO && d instanceof AdaptiveIconDrawable) {
+            if (mAdaptiveIconScale == SCALE_NOT_INITIALIZED) {
+                mAdaptiveIconScale = normalizeAdaptiveIcon(d, mMaxSize, mAdaptiveIconBounds);
+            }
+            if (outBounds != null) {
+                outBounds.set(mAdaptiveIconBounds);
+            }
+            return mAdaptiveIconScale;
+        }
+        int width = d.getIntrinsicWidth();
+        int height = d.getIntrinsicHeight();
+        if (width <= 0 || height <= 0) {
+            width = width <= 0 || width > mMaxSize ? mMaxSize : width;
+            height = height <= 0 || height > mMaxSize ? mMaxSize : height;
+        } else if (width > mMaxSize || height > mMaxSize) {
+            int max = Math.max(width, height);
+            width = mMaxSize * width / max;
+            height = mMaxSize * height / max;
+        }
+
+        mBitmap.eraseColor(Color.TRANSPARENT);
+        d.setBounds(0, 0, width, height);
+        d.draw(mCanvas);
+
+        ByteBuffer buffer = ByteBuffer.wrap(mPixels);
+        buffer.rewind();
+        mBitmap.copyPixelsToBuffer(buffer);
+
+        // Overall bounds of the visible icon.
+        int topY = -1;
+        int bottomY = -1;
+        int leftX = mMaxSize + 1;
+        int rightX = -1;
+
+        // Create border by going through all pixels one row at a time and for each row find
+        // the first and the last non-transparent pixel. Set those values to mLeftBorder and
+        // mRightBorder and use -1 if there are no visible pixel in the row.
+
+        // buffer position
+        int index = 0;
+        // buffer shift after every row, width of buffer = mMaxSize
+        int rowSizeDiff = mMaxSize - width;
+        // first and last position for any row.
+        int firstX, lastX;
+
+        for (int y = 0; y < height; y++) {
+            firstX = lastX = -1;
+            for (int x = 0; x < width; x++) {
+                if ((mPixels[index] & 0xFF) > MIN_VISIBLE_ALPHA) {
+                    if (firstX == -1) {
+                        firstX = x;
+                    }
+                    lastX = x;
+                }
+                index++;
+            }
+            index += rowSizeDiff;
+
+            mLeftBorder[y] = firstX;
+            mRightBorder[y] = lastX;
+
+            // If there is at least one visible pixel, update the overall bounds.
+            if (firstX != -1) {
+                bottomY = y;
+                if (topY == -1) {
+                    topY = y;
+                }
+
+                leftX = Math.min(leftX, firstX);
+                rightX = Math.max(rightX, lastX);
+            }
+        }
+
+        if (topY == -1 || rightX == -1) {
+            // No valid pixels found. Do not scale.
+            return 1;
+        }
+
+        convertToConvexArray(mLeftBorder, 1, topY, bottomY);
+        convertToConvexArray(mRightBorder, -1, topY, bottomY);
+
+        // Area of the convex hull
+        float area = 0;
+        for (int y = 0; y < height; y++) {
+            if (mLeftBorder[y] <= -1) {
+                continue;
+            }
+            area += mRightBorder[y] - mLeftBorder[y] + 1;
+        }
+
+        mBounds.left = leftX;
+        mBounds.right = rightX;
+
+        mBounds.top = topY;
+        mBounds.bottom = bottomY;
+
+        if (outBounds != null) {
+            outBounds.set(((float) mBounds.left) / width, ((float) mBounds.top) / height,
+                    1 - ((float) mBounds.right) / width,
+                    1 - ((float) mBounds.bottom) / height);
+        }
+        if (outMaskShape != null && mEnableShapeDetection && outMaskShape.length > 0) {
+            outMaskShape[0] = isShape(path);
+        }
+        // Area of the rectangle required to fit the convex hull
+        float rectArea = (bottomY + 1 - topY) * (rightX + 1 - leftX);
+        return getScale(area, rectArea, width * height);
+    }
+
+    /**
+     * Modifies {@param xCoordinates} to represent a convex border. Fills in all missing values
+     * (except on either ends) with appropriate values.
+     * @param xCoordinates map of x coordinate per y.
+     * @param direction 1 for left border and -1 for right border.
+     * @param topY the first Y position (inclusive) with a valid value.
+     * @param bottomY the last Y position (inclusive) with a valid value.
+     */
+    private static void convertToConvexArray(
+            float[] xCoordinates, int direction, int topY, int bottomY) {
+        int total = xCoordinates.length;
+        // The tangent at each pixel.
+        float[] angles = new float[total - 1];
+
+        int first = topY; // First valid y coordinate
+        int last = -1;    // Last valid y coordinate which didn't have a missing value
+
+        float lastAngle = Float.MAX_VALUE;
+
+        for (int i = topY + 1; i <= bottomY; i++) {
+            if (xCoordinates[i] <= -1) {
+                continue;
+            }
+            int start;
+
+            if (lastAngle == Float.MAX_VALUE) {
+                start = first;
+            } else {
+                float currentAngle = (xCoordinates[i] - xCoordinates[last]) / (i - last);
+                start = last;
+                // If this position creates a concave angle, keep moving up until we find a
+                // position which creates a convex angle.
+                if ((currentAngle - lastAngle) * direction < 0) {
+                    while (start > first) {
+                        start --;
+                        currentAngle = (xCoordinates[i] - xCoordinates[start]) / (i - start);
+                        if ((currentAngle - angles[start]) * direction >= 0) {
+                            break;
+                        }
+                    }
+                }
+            }
+
+            // Reset from last check
+            lastAngle = (xCoordinates[i] - xCoordinates[start]) / (i - start);
+            // Update all the points from start.
+            for (int j = start; j < i; j++) {
+                angles[j] = lastAngle;
+                xCoordinates[j] = xCoordinates[start] + lastAngle * (j - start);
+            }
+            last = i;
+        }
+    }
+
+    /**
+     * @return The diameter of the normalized circle that fits inside of the square (size x size).
+     */
+    public static int getNormalizedCircleSize(int size) {
+        float area = size * size * MAX_CIRCLE_AREA_FACTOR;
+        return (int) Math.round(Math.sqrt((4 * area) / Math.PI));
+    }
+}
diff --git a/iconloaderlib/src/com/android/launcher3/icons/ShadowGenerator.java b/iconloaderlib/src/com/android/launcher3/icons/ShadowGenerator.java
new file mode 100644
index 0000000..5df8043
--- /dev/null
+++ b/iconloaderlib/src/com/android/launcher3/icons/ShadowGenerator.java
@@ -0,0 +1,170 @@
+/*
+ * 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.icons;
+
+import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
+
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.BlurMaskFilter;
+import android.graphics.BlurMaskFilter.Blur;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.RectF;
+
+/**
+ * Utility class to add shadows to bitmaps.
+ */
+public class ShadowGenerator {
+    public static final float BLUR_FACTOR = 0.5f/48;
+
+    // Percent of actual icon size
+    public static final float KEY_SHADOW_DISTANCE = 1f/48;
+    private static final int KEY_SHADOW_ALPHA = 61;
+    // Percent of actual icon size
+    private static final float HALF_DISTANCE = 0.5f;
+    private static final int AMBIENT_SHADOW_ALPHA = 30;
+
+    private final int mIconSize;
+
+    private final Paint mBlurPaint;
+    private final Paint mDrawPaint;
+    private final BlurMaskFilter mDefaultBlurMaskFilter;
+
+    public ShadowGenerator(int iconSize) {
+        mIconSize = iconSize;
+        mBlurPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+        mDrawPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+        mDefaultBlurMaskFilter = new BlurMaskFilter(mIconSize * BLUR_FACTOR, Blur.NORMAL);
+    }
+
+    public synchronized void recreateIcon(Bitmap icon, Canvas out) {
+        recreateIcon(icon, mDefaultBlurMaskFilter, AMBIENT_SHADOW_ALPHA, KEY_SHADOW_ALPHA, out);
+    }
+
+    public synchronized void recreateIcon(Bitmap icon, BlurMaskFilter blurMaskFilter,
+            int ambientAlpha, int keyAlpha, Canvas out) {
+        int[] offset = new int[2];
+        mBlurPaint.setMaskFilter(blurMaskFilter);
+        Bitmap shadow = icon.extractAlpha(mBlurPaint, offset);
+
+        // Draw ambient shadow
+        mDrawPaint.setAlpha(ambientAlpha);
+        out.drawBitmap(shadow, offset[0], offset[1], mDrawPaint);
+
+        // Draw key shadow
+        mDrawPaint.setAlpha(keyAlpha);
+        out.drawBitmap(shadow, offset[0], offset[1] + KEY_SHADOW_DISTANCE * mIconSize, mDrawPaint);
+
+        // Draw the icon
+        mDrawPaint.setAlpha(255);
+        out.drawBitmap(icon, 0, 0, mDrawPaint);
+    }
+
+    /**
+     * Returns the minimum amount by which an icon with {@param bounds} should be scaled
+     * so that the shadows do not get clipped.
+     */
+    public static float getScaleForBounds(RectF bounds) {
+        float scale = 1;
+
+        // For top, left & right, we need same space.
+        float minSide = Math.min(Math.min(bounds.left, bounds.right), bounds.top);
+        if (minSide < BLUR_FACTOR) {
+            scale = (HALF_DISTANCE - BLUR_FACTOR) / (HALF_DISTANCE - minSide);
+        }
+
+        float bottomSpace = BLUR_FACTOR + KEY_SHADOW_DISTANCE;
+        if (bounds.bottom < bottomSpace) {
+            scale = Math.min(scale, (HALF_DISTANCE - bottomSpace) / (HALF_DISTANCE - bounds.bottom));
+        }
+        return scale;
+    }
+
+    public static class Builder {
+
+        public final RectF bounds = new RectF();
+        public final int color;
+
+        public int ambientShadowAlpha = AMBIENT_SHADOW_ALPHA;
+
+        public float shadowBlur;
+
+        public float keyShadowDistance;
+        public int keyShadowAlpha = KEY_SHADOW_ALPHA;
+        public float radius;
+
+        public Builder(int color) {
+            this.color = color;
+        }
+
+        public Builder setupBlurForSize(int height) {
+            shadowBlur = height * 1f / 24;
+            keyShadowDistance = height * 1f / 16;
+            return this;
+        }
+
+        public Bitmap createPill(int width, int height) {
+            return createPill(width, height, height / 2f);
+        }
+
+        public Bitmap createPill(int width, int height, float r) {
+            radius = r;
+
+            int centerX = Math.round(width / 2f + shadowBlur);
+            int centerY = Math.round(radius + shadowBlur + keyShadowDistance);
+            int center = Math.max(centerX, centerY);
+            bounds.set(0, 0, width, height);
+            bounds.offsetTo(center - width / 2f, center - height / 2f);
+
+            int size = center * 2;
+            Bitmap result = Bitmap.createBitmap(size, size, Config.ARGB_8888);
+            drawShadow(new Canvas(result));
+            return result;
+        }
+
+        public void drawShadow(Canvas c) {
+            Paint p = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+            p.setColor(color);
+
+            // Key shadow
+            p.setShadowLayer(shadowBlur, 0, keyShadowDistance,
+                    setColorAlphaBound(Color.BLACK, keyShadowAlpha));
+            c.drawRoundRect(bounds, radius, radius, p);
+
+            // Ambient shadow
+            p.setShadowLayer(shadowBlur, 0, 0,
+                    setColorAlphaBound(Color.BLACK, ambientShadowAlpha));
+            c.drawRoundRect(bounds, radius, radius, p);
+
+            if (Color.alpha(color) < 255) {
+                // Clear any content inside the pill-rect for translucent fill.
+                p.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
+                p.clearShadowLayer();
+                p.setColor(Color.BLACK);
+                c.drawRoundRect(bounds, radius, radius, p);
+
+                p.setXfermode(null);
+                p.setColor(color);
+                c.drawRoundRect(bounds, radius, radius, p);
+            }
+        }
+    }
+}
diff --git a/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java b/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java
new file mode 100644
index 0000000..d84633d
--- /dev/null
+++ b/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java
@@ -0,0 +1,563 @@
+/*
+ * 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.icons.cache;
+
+import static com.android.launcher3.icons.BaseIconFactory.getFullResDefaultActivityIcon;
+import static com.android.launcher3.icons.BitmapInfo.LOW_RES_ICON;
+import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
+
+import android.content.ComponentName;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteException;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Process;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.launcher3.icons.BaseIconFactory;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.BitmapRenderer;
+import com.android.launcher3.icons.GraphicsUtils;
+import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.SQLiteCacheHelper;
+
+import java.util.AbstractMap;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Supplier;
+
+import androidx.annotation.NonNull;
+
+public abstract class BaseIconCache {
+
+    private static final String TAG = "BaseIconCache";
+    private static final boolean DEBUG = false;
+
+    private static final int INITIAL_ICON_CACHE_CAPACITY = 50;
+
+    // Empty class name is used for storing package default entry.
+    public static final String EMPTY_CLASS_NAME = ".";
+
+    public static class CacheEntry extends BitmapInfo {
+        public CharSequence title = "";
+        public CharSequence contentDescription = "";
+    }
+
+    private final HashMap<UserHandle, BitmapInfo> mDefaultIcons = new HashMap<>();
+
+    protected final Context mContext;
+    protected final PackageManager mPackageManager;
+
+    private final Map<ComponentKey, CacheEntry> mCache;
+    protected final Handler mWorkerHandler;
+
+    protected int mIconDpi;
+    protected IconDB mIconDb;
+    protected String mSystemState = "";
+
+    private final String mDbFileName;
+    private final BitmapFactory.Options mDecodeOptions;
+    private final Looper mBgLooper;
+
+    public BaseIconCache(Context context, String dbFileName, Looper bgLooper,
+            int iconDpi, int iconPixelSize, boolean inMemoryCache) {
+        mContext = context;
+        mDbFileName = dbFileName;
+        mPackageManager = context.getPackageManager();
+        mBgLooper = bgLooper;
+        mWorkerHandler = new Handler(mBgLooper);
+
+        if (inMemoryCache) {
+            mCache = new HashMap<>(INITIAL_ICON_CACHE_CAPACITY);
+        } else {
+            // Use a dummy cache
+            mCache = new AbstractMap<ComponentKey, CacheEntry>() {
+                @Override
+                public Set<Entry<ComponentKey, CacheEntry>> entrySet() {
+                    return Collections.emptySet();
+                }
+
+                @Override
+                public CacheEntry put(ComponentKey key, CacheEntry value) {
+                    return value;
+                }
+            };
+        }
+
+        if (BitmapRenderer.USE_HARDWARE_BITMAP && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            mDecodeOptions = new BitmapFactory.Options();
+            mDecodeOptions.inPreferredConfig = Bitmap.Config.HARDWARE;
+        } else {
+            mDecodeOptions = null;
+        }
+
+        updateSystemState();
+        mIconDpi = iconDpi;
+        mIconDb = new IconDB(context, dbFileName, iconPixelSize);
+    }
+
+    /**
+     * Returns the persistable serial number for {@param user}. Subclass should implement proper
+     * caching strategy to avoid making binder call every time.
+     */
+    protected abstract long getSerialNumberForUser(UserHandle user);
+
+    /**
+     * Return true if the given app is an instant app and should be badged appropriately.
+     */
+    protected abstract boolean isInstantApp(ApplicationInfo info);
+
+    /**
+     * Opens and returns an icon factory. The factory is recycled by the caller.
+     */
+    protected abstract BaseIconFactory getIconFactory();
+
+    public void updateIconParams(int iconDpi, int iconPixelSize) {
+        mWorkerHandler.post(() -> updateIconParamsBg(iconDpi, iconPixelSize));
+    }
+
+    private synchronized void updateIconParamsBg(int iconDpi, int iconPixelSize) {
+        mIconDpi = iconDpi;
+        mDefaultIcons.clear();
+        mIconDb.clear();
+        mIconDb.close();
+        mIconDb = new IconDB(mContext, mDbFileName, iconPixelSize);
+        mCache.clear();
+    }
+
+    private Drawable getFullResIcon(Resources resources, int iconId) {
+        if (resources != null && iconId != 0) {
+            try {
+                return resources.getDrawableForDensity(iconId, mIconDpi);
+            } catch (Resources.NotFoundException e) { }
+        }
+        return getFullResDefaultActivityIcon(mIconDpi);
+    }
+
+    public Drawable getFullResIcon(String packageName, int iconId) {
+        try {
+            return getFullResIcon(mPackageManager.getResourcesForApplication(packageName), iconId);
+        } catch (PackageManager.NameNotFoundException e) { }
+        return getFullResDefaultActivityIcon(mIconDpi);
+    }
+
+    public Drawable getFullResIcon(ActivityInfo info) {
+        try {
+            return getFullResIcon(mPackageManager.getResourcesForApplication(info.applicationInfo),
+                    info.getIconResource());
+        } catch (PackageManager.NameNotFoundException e) { }
+        return getFullResDefaultActivityIcon(mIconDpi);
+    }
+
+    private BitmapInfo makeDefaultIcon(UserHandle user) {
+        try (BaseIconFactory li = getIconFactory()) {
+            return li.makeDefaultIcon(user);
+        }
+    }
+
+    /**
+     * Remove any records for the supplied ComponentName.
+     */
+    public synchronized void remove(ComponentName componentName, UserHandle user) {
+        mCache.remove(new ComponentKey(componentName, user));
+    }
+
+    /**
+     * Remove any records for the supplied package name from memory.
+     */
+    private void removeFromMemCacheLocked(String packageName, UserHandle user) {
+        HashSet<ComponentKey> forDeletion = new HashSet<>();
+        for (ComponentKey key: mCache.keySet()) {
+            if (key.componentName.getPackageName().equals(packageName)
+                    && key.user.equals(user)) {
+                forDeletion.add(key);
+            }
+        }
+        for (ComponentKey condemned: forDeletion) {
+            mCache.remove(condemned);
+        }
+    }
+
+    /**
+     * Removes the entries related to the given package in memory and persistent DB.
+     */
+    public synchronized void removeIconsForPkg(String packageName, UserHandle user) {
+        removeFromMemCacheLocked(packageName, user);
+        long userSerial = getSerialNumberForUser(user);
+        mIconDb.delete(
+                IconDB.COLUMN_COMPONENT + " LIKE ? AND " + IconDB.COLUMN_USER + " = ?",
+                new String[]{packageName + "/%", Long.toString(userSerial)});
+    }
+
+    public IconCacheUpdateHandler getUpdateHandler() {
+        updateSystemState();
+        return new IconCacheUpdateHandler(this);
+    }
+
+    /**
+     * Refreshes the system state definition used to check the validity of the cache. It
+     * incorporates all the properties that can affect the cache like locale and system-version.
+     */
+    private void updateSystemState() {
+        final String locale =
+                mContext.getResources().getConfiguration().getLocales().toLanguageTags();
+        mSystemState = locale + "," + Build.VERSION.SDK_INT;
+    }
+
+    protected String getIconSystemState(String packageName) {
+        return mSystemState;
+    }
+
+    /**
+     * Adds an entry into the DB and the in-memory cache.
+     * @param replaceExisting if true, it will recreate the bitmap even if it already exists in
+     *                        the memory. This is useful then the previous bitmap was created using
+     *                        old data.
+     * package private
+     */
+    protected synchronized <T> void addIconToDBAndMemCache(T object, CachingLogic<T> cachingLogic,
+            PackageInfo info, long userSerial, boolean replaceExisting) {
+        UserHandle user = cachingLogic.getUser(object);
+        ComponentName componentName = cachingLogic.getComponent(object);
+
+        final ComponentKey key = new ComponentKey(componentName, user);
+        CacheEntry entry = null;
+        if (!replaceExisting) {
+            entry = mCache.get(key);
+            // We can't reuse the entry if the high-res icon is not present.
+            if (entry == null || entry.icon == null || entry.isLowRes()) {
+                entry = null;
+            }
+        }
+        if (entry == null) {
+            entry = new CacheEntry();
+            cachingLogic.loadIcon(mContext, object, entry);
+        }
+        entry.title = cachingLogic.getLabel(object);
+        entry.contentDescription = mPackageManager.getUserBadgedLabel(entry.title, user);
+        mCache.put(key, entry);
+
+        ContentValues values = newContentValues(entry, entry.title.toString(),
+                componentName.getPackageName());
+        addIconToDB(values, componentName, info, userSerial);
+    }
+
+    /**
+     * Updates {@param values} to contain versioning information and adds it to the DB.
+     * @param values {@link ContentValues} containing icon & title
+     */
+    private void addIconToDB(ContentValues values, ComponentName key,
+            PackageInfo info, long userSerial) {
+        values.put(IconDB.COLUMN_COMPONENT, key.flattenToString());
+        values.put(IconDB.COLUMN_USER, userSerial);
+        values.put(IconDB.COLUMN_LAST_UPDATED, info.lastUpdateTime);
+        values.put(IconDB.COLUMN_VERSION, info.versionCode);
+        mIconDb.insertOrReplace(values);
+    }
+
+    public synchronized BitmapInfo getDefaultIcon(UserHandle user) {
+        if (!mDefaultIcons.containsKey(user)) {
+            mDefaultIcons.put(user, makeDefaultIcon(user));
+        }
+        return mDefaultIcons.get(user);
+    }
+
+    public boolean isDefaultIcon(Bitmap icon, UserHandle user) {
+        return getDefaultIcon(user).icon == icon;
+    }
+
+    /**
+     * Retrieves the entry from the cache. If the entry is not present, it creates a new entry.
+     * This method is not thread safe, it must be called from a synchronized method.
+     */
+    protected <T> CacheEntry cacheLocked(
+            @NonNull ComponentName componentName, @NonNull UserHandle user,
+            @NonNull Supplier<T> infoProvider, @NonNull CachingLogic<T> cachingLogic,
+            boolean usePackageIcon, boolean useLowResIcon) {
+        return cacheLocked(componentName, user, infoProvider, cachingLogic, usePackageIcon,
+                useLowResIcon, true);
+    }
+
+    protected <T> CacheEntry cacheLocked(
+            @NonNull ComponentName componentName, @NonNull UserHandle user,
+            @NonNull Supplier<T> infoProvider, @NonNull CachingLogic<T> cachingLogic,
+            boolean usePackageIcon, boolean useLowResIcon, boolean addToMemCache) {
+        assertWorkerThread();
+        ComponentKey cacheKey = new ComponentKey(componentName, user);
+        CacheEntry entry = mCache.get(cacheKey);
+        if (entry == null || (entry.isLowRes() && !useLowResIcon)) {
+            entry = new CacheEntry();
+            if (addToMemCache) {
+                mCache.put(cacheKey, entry);
+            }
+
+            // Check the DB first.
+            T object = null;
+            boolean providerFetchedOnce = false;
+
+            if (!getEntryFromDB(cacheKey, entry, useLowResIcon)) {
+                object = infoProvider.get();
+                providerFetchedOnce = true;
+
+                if (object != null) {
+                    cachingLogic.loadIcon(mContext, object, entry);
+                } else {
+                    if (usePackageIcon) {
+                        CacheEntry packageEntry = getEntryForPackageLocked(
+                                componentName.getPackageName(), user, false);
+                        if (packageEntry != null) {
+                            if (DEBUG) Log.d(TAG, "using package default icon for " +
+                                    componentName.toShortString());
+                            packageEntry.applyTo(entry);
+                            entry.title = packageEntry.title;
+                            entry.contentDescription = packageEntry.contentDescription;
+                        }
+                    }
+                    if (entry.icon == null) {
+                        if (DEBUG) Log.d(TAG, "using default icon for " +
+                                componentName.toShortString());
+                        getDefaultIcon(user).applyTo(entry);
+                    }
+                }
+            }
+
+            if (TextUtils.isEmpty(entry.title)) {
+                if (object == null && !providerFetchedOnce) {
+                    object = infoProvider.get();
+                    providerFetchedOnce = true;
+                }
+                if (object != null) {
+                    entry.title = cachingLogic.getLabel(object);
+                    entry.contentDescription = mPackageManager.getUserBadgedLabel(entry.title, user);
+                }
+            }
+        }
+        return entry;
+    }
+
+    public synchronized void clear() {
+        assertWorkerThread();
+        mIconDb.clear();
+    }
+
+    /**
+     * Adds a default package entry in the cache. This entry is not persisted and will be removed
+     * when the cache is flushed.
+     */
+    public synchronized void cachePackageInstallInfo(String packageName, UserHandle user,
+            Bitmap icon, CharSequence title) {
+        removeFromMemCacheLocked(packageName, user);
+
+        ComponentKey cacheKey = getPackageKey(packageName, user);
+        CacheEntry entry = mCache.get(cacheKey);
+
+        // For icon caching, do not go through DB. Just update the in-memory entry.
+        if (entry == null) {
+            entry = new CacheEntry();
+        }
+        if (!TextUtils.isEmpty(title)) {
+            entry.title = title;
+        }
+        if (icon != null) {
+            BaseIconFactory li = getIconFactory();
+            li.createIconBitmap(icon).applyTo(entry);
+            li.close();
+        }
+        if (!TextUtils.isEmpty(title) && entry.icon != null) {
+            mCache.put(cacheKey, entry);
+        }
+    }
+
+    private static ComponentKey getPackageKey(String packageName, UserHandle user) {
+        ComponentName cn = new ComponentName(packageName, packageName + EMPTY_CLASS_NAME);
+        return new ComponentKey(cn, user);
+    }
+
+    /**
+     * Gets an entry for the package, which can be used as a fallback entry for various components.
+     * This method is not thread safe, it must be called from a synchronized method.
+     */
+    protected CacheEntry getEntryForPackageLocked(String packageName, UserHandle user,
+            boolean useLowResIcon) {
+        assertWorkerThread();
+        ComponentKey cacheKey = getPackageKey(packageName, user);
+        CacheEntry entry = mCache.get(cacheKey);
+
+        if (entry == null || (entry.isLowRes() && !useLowResIcon)) {
+            entry = new CacheEntry();
+            boolean entryUpdated = true;
+
+            // Check the DB first.
+            if (!getEntryFromDB(cacheKey, entry, useLowResIcon)) {
+                try {
+                    int flags = Process.myUserHandle().equals(user) ? 0 :
+                            PackageManager.GET_UNINSTALLED_PACKAGES;
+                    PackageInfo info = mPackageManager.getPackageInfo(packageName, flags);
+                    ApplicationInfo appInfo = info.applicationInfo;
+                    if (appInfo == null) {
+                        throw new NameNotFoundException("ApplicationInfo is null");
+                    }
+
+                    BaseIconFactory li = getIconFactory();
+                    // Load the full res icon for the application, but if useLowResIcon is set, then
+                    // only keep the low resolution icon instead of the larger full-sized icon
+                    BitmapInfo iconInfo = li.createBadgedIconBitmap(
+                            appInfo.loadIcon(mPackageManager), user, appInfo.targetSdkVersion,
+                            isInstantApp(appInfo));
+                    li.close();
+
+                    entry.title = appInfo.loadLabel(mPackageManager);
+                    entry.contentDescription = mPackageManager.getUserBadgedLabel(entry.title, user);
+                    entry.icon = useLowResIcon ? LOW_RES_ICON : iconInfo.icon;
+                    entry.color = iconInfo.color;
+
+                    // Add the icon in the DB here, since these do not get written during
+                    // package updates.
+                    ContentValues values = newContentValues(
+                            iconInfo, entry.title.toString(), packageName);
+                    addIconToDB(values, cacheKey.componentName, info, getSerialNumberForUser(user));
+
+                } catch (NameNotFoundException e) {
+                    if (DEBUG) Log.d(TAG, "Application not installed " + packageName);
+                    entryUpdated = false;
+                }
+            }
+
+            // Only add a filled-out entry to the cache
+            if (entryUpdated) {
+                mCache.put(cacheKey, entry);
+            }
+        }
+        return entry;
+    }
+
+    private boolean getEntryFromDB(ComponentKey cacheKey, CacheEntry entry, boolean lowRes) {
+        Cursor c = null;
+        try {
+            c = mIconDb.query(
+                    lowRes ? IconDB.COLUMNS_LOW_RES : IconDB.COLUMNS_HIGH_RES,
+                    IconDB.COLUMN_COMPONENT + " = ? AND " + IconDB.COLUMN_USER + " = ?",
+                    new String[]{
+                            cacheKey.componentName.flattenToString(),
+                            Long.toString(getSerialNumberForUser(cacheKey.user))});
+            if (c.moveToNext()) {
+                // Set the alpha to be 255, so that we never have a wrong color
+                entry.color = setColorAlphaBound(c.getInt(0), 255);
+                entry.title = c.getString(1);
+                if (entry.title == null) {
+                    entry.title = "";
+                    entry.contentDescription = "";
+                } else {
+                    entry.contentDescription = mPackageManager.getUserBadgedLabel(
+                            entry.title, cacheKey.user);
+                }
+
+                if (lowRes) {
+                    entry.icon = LOW_RES_ICON;
+                } else {
+                    byte[] data = c.getBlob(2);
+                    try {
+                        entry.icon = BitmapFactory.decodeByteArray(data, 0, data.length,
+                                mDecodeOptions);
+                    } catch (Exception e) { }
+                }
+                return true;
+            }
+        } catch (SQLiteException e) {
+            Log.d(TAG, "Error reading icon cache", e);
+        } finally {
+            if (c != null) {
+                c.close();
+            }
+        }
+        return false;
+    }
+
+    static final class IconDB extends SQLiteCacheHelper {
+        private final static int RELEASE_VERSION = 26;
+
+        public final static String TABLE_NAME = "icons";
+        public final static String COLUMN_ROWID = "rowid";
+        public final static String COLUMN_COMPONENT = "componentName";
+        public final static String COLUMN_USER = "profileId";
+        public final static String COLUMN_LAST_UPDATED = "lastUpdated";
+        public final static String COLUMN_VERSION = "version";
+        public final static String COLUMN_ICON = "icon";
+        public final static String COLUMN_ICON_COLOR = "icon_color";
+        public final static String COLUMN_LABEL = "label";
+        public final static String COLUMN_SYSTEM_STATE = "system_state";
+
+        public final static String[] COLUMNS_HIGH_RES = new String[] {
+                IconDB.COLUMN_ICON_COLOR, IconDB.COLUMN_LABEL, IconDB.COLUMN_ICON };
+        public final static String[] COLUMNS_LOW_RES = new String[] {
+                IconDB.COLUMN_ICON_COLOR, IconDB.COLUMN_LABEL };
+
+        public IconDB(Context context, String dbFileName, int iconPixelSize) {
+            super(context, dbFileName, (RELEASE_VERSION << 16) + iconPixelSize, TABLE_NAME);
+        }
+
+        @Override
+        protected void onCreateTable(SQLiteDatabase db) {
+            db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" +
+                    COLUMN_COMPONENT + " TEXT NOT NULL, " +
+                    COLUMN_USER + " INTEGER NOT NULL, " +
+                    COLUMN_LAST_UPDATED + " INTEGER NOT NULL DEFAULT 0, " +
+                    COLUMN_VERSION + " INTEGER NOT NULL DEFAULT 0, " +
+                    COLUMN_ICON + " BLOB, " +
+                    COLUMN_ICON_COLOR + " INTEGER NOT NULL DEFAULT 0, " +
+                    COLUMN_LABEL + " TEXT, " +
+                    COLUMN_SYSTEM_STATE + " TEXT, " +
+                    "PRIMARY KEY (" + COLUMN_COMPONENT + ", " + COLUMN_USER + ") " +
+                    ");");
+        }
+    }
+
+    private ContentValues newContentValues(BitmapInfo bitmapInfo, String label, String packageName) {
+        ContentValues values = new ContentValues();
+        values.put(IconDB.COLUMN_ICON,
+                bitmapInfo.isLowRes() ? null : GraphicsUtils.flattenBitmap(bitmapInfo.icon));
+        values.put(IconDB.COLUMN_ICON_COLOR, bitmapInfo.color);
+
+        values.put(IconDB.COLUMN_LABEL, label);
+        values.put(IconDB.COLUMN_SYSTEM_STATE, getIconSystemState(packageName));
+
+        return values;
+    }
+
+    private void assertWorkerThread() {
+        if (Looper.myLooper() != mBgLooper) {
+            throw new IllegalStateException("Cache accessed on wrong thread " + Looper.myLooper());
+        }
+    }
+}
diff --git a/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java b/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java
new file mode 100644
index 0000000..addb51f
--- /dev/null
+++ b/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java
@@ -0,0 +1,33 @@
+/*
+ * 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.icons.cache;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.UserHandle;
+
+import com.android.launcher3.icons.BitmapInfo;
+
+public interface CachingLogic<T> {
+
+    ComponentName getComponent(T object);
+
+    UserHandle getUser(T object);
+
+    CharSequence getLabel(T object);
+
+    void loadIcon(Context context, T object, BitmapInfo target);
+}
diff --git a/iconloaderlib/src/com/android/launcher3/icons/cache/HandlerRunnable.java b/iconloaderlib/src/com/android/launcher3/icons/cache/HandlerRunnable.java
new file mode 100644
index 0000000..ee52934
--- /dev/null
+++ b/iconloaderlib/src/com/android/launcher3/icons/cache/HandlerRunnable.java
@@ -0,0 +1,67 @@
+/*
+ * 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.icons.cache;
+
+import android.os.Handler;
+
+/**
+ * A runnable that can be posted to a {@link Handler} which can be canceled.
+ */
+public abstract class HandlerRunnable implements Runnable {
+
+    private final Handler mHandler;
+    private final Runnable mEndRunnable;
+
+    private boolean mEnded = false;
+    private boolean mCanceled = false;
+
+    public HandlerRunnable(Handler handler, Runnable endRunnable) {
+        mHandler = handler;
+        mEndRunnable = endRunnable;
+    }
+
+    /**
+     * Cancels this runnable from being run, only if it has not already run.
+     */
+    public void cancel() {
+        mHandler.removeCallbacks(this);
+        // TODO: This can actually cause onEnd to be called twice if the handler is already running
+        //       this runnable
+        // NOTE: This is currently run on whichever thread the caller is run on.
+        mCanceled = true;
+        onEnd();
+    }
+
+    /**
+     * @return whether this runnable was canceled.
+     */
+    protected boolean isCanceled() {
+        return mCanceled;
+    }
+
+    /**
+     * To be called by the implemention of this runnable. The end callback is done on whichever
+     * thread the caller is calling from.
+     */
+    public void onEnd() {
+        if (!mEnded) {
+            mEnded = true;
+            if (mEndRunnable != null) {
+                mEndRunnable.run();
+            }
+        }
+    }
+}
diff --git a/iconloaderlib/src/com/android/launcher3/icons/cache/IconCacheUpdateHandler.java b/iconloaderlib/src/com/android/launcher3/icons/cache/IconCacheUpdateHandler.java
new file mode 100644
index 0000000..3c71bd0
--- /dev/null
+++ b/iconloaderlib/src/com/android/launcher3/icons/cache/IconCacheUpdateHandler.java
@@ -0,0 +1,303 @@
+/*
+ * 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.icons.cache;
+
+import android.content.ComponentName;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.SparseBooleanArray;
+
+import com.android.launcher3.icons.cache.BaseIconCache.IconDB;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.Stack;
+
+/**
+ * Utility class to handle updating the Icon cache
+ */
+public class IconCacheUpdateHandler {
+
+    private static final String TAG = "IconCacheUpdateHandler";
+
+    /**
+     * In this mode, all invalid icons are marked as to-be-deleted in {@link #mItemsToDelete}.
+     * This mode is used for the first run.
+     */
+    private static final boolean MODE_SET_INVALID_ITEMS = true;
+
+    /**
+     * In this mode, any valid icon is removed from {@link #mItemsToDelete}. This is used for all
+     * subsequent runs, which essentially acts as set-union of all valid items.
+     */
+    private static final boolean MODE_CLEAR_VALID_ITEMS = false;
+
+    private static final Object ICON_UPDATE_TOKEN = new Object();
+
+    private final HashMap<String, PackageInfo> mPkgInfoMap;
+    private final BaseIconCache mIconCache;
+
+    private final HashMap<UserHandle, Set<String>> mPackagesToIgnore = new HashMap<>();
+
+    private final SparseBooleanArray mItemsToDelete = new SparseBooleanArray();
+    private boolean mFilterMode = MODE_SET_INVALID_ITEMS;
+
+    IconCacheUpdateHandler(BaseIconCache cache) {
+        mIconCache = cache;
+
+        mPkgInfoMap = new HashMap<>();
+
+        // Remove all active icon update tasks.
+        mIconCache.mWorkerHandler.removeCallbacksAndMessages(ICON_UPDATE_TOKEN);
+
+        createPackageInfoMap();
+    }
+
+    public void setPackagesToIgnore(UserHandle userHandle, Set<String> packages) {
+        mPackagesToIgnore.put(userHandle, packages);
+    }
+
+    private void createPackageInfoMap() {
+        PackageManager pm = mIconCache.mPackageManager;
+        for (PackageInfo info :
+                pm.getInstalledPackages(PackageManager.MATCH_UNINSTALLED_PACKAGES)) {
+            mPkgInfoMap.put(info.packageName, info);
+        }
+    }
+
+    /**
+     * Updates the persistent DB, such that only entries corresponding to {@param apps} remain in
+     * the DB and are updated.
+     * @return The set of packages for which icons have updated.
+     */
+    public <T> void updateIcons(List<T> apps, CachingLogic<T> cachingLogic,
+            OnUpdateCallback onUpdateCallback) {
+        // Filter the list per user
+        HashMap<UserHandle, HashMap<ComponentName, T>> userComponentMap = new HashMap<>();
+        int count = apps.size();
+        for (int i = 0; i < count; i++) {
+            T app = apps.get(i);
+            UserHandle userHandle = cachingLogic.getUser(app);
+            HashMap<ComponentName, T> componentMap = userComponentMap.get(userHandle);
+            if (componentMap == null) {
+                componentMap = new HashMap<>();
+                userComponentMap.put(userHandle, componentMap);
+            }
+            componentMap.put(cachingLogic.getComponent(app), app);
+        }
+
+        for (Entry<UserHandle, HashMap<ComponentName, T>> entry : userComponentMap.entrySet()) {
+            updateIconsPerUser(entry.getKey(), entry.getValue(), cachingLogic, onUpdateCallback);
+        }
+
+        // From now on, clear every valid item from the global valid map.
+        mFilterMode = MODE_CLEAR_VALID_ITEMS;
+    }
+
+    /**
+     * Updates the persistent DB, such that only entries corresponding to {@param apps} remain in
+     * the DB and are updated.
+     * @return The set of packages for which icons have updated.
+     */
+    @SuppressWarnings("unchecked")
+    private <T> void updateIconsPerUser(UserHandle user, HashMap<ComponentName, T> componentMap,
+            CachingLogic<T> cachingLogic, OnUpdateCallback onUpdateCallback) {
+        Set<String> ignorePackages = mPackagesToIgnore.get(user);
+        if (ignorePackages == null) {
+            ignorePackages = Collections.emptySet();
+        }
+        long userSerial = mIconCache.getSerialNumberForUser(user);
+
+        Stack<T> appsToUpdate = new Stack<>();
+
+        try (Cursor c = mIconCache.mIconDb.query(
+                new String[]{IconDB.COLUMN_ROWID, IconDB.COLUMN_COMPONENT,
+                        IconDB.COLUMN_LAST_UPDATED, IconDB.COLUMN_VERSION,
+                        IconDB.COLUMN_SYSTEM_STATE},
+                IconDB.COLUMN_USER + " = ? ",
+                new String[]{Long.toString(userSerial)})) {
+
+            final int indexComponent = c.getColumnIndex(IconDB.COLUMN_COMPONENT);
+            final int indexLastUpdate = c.getColumnIndex(IconDB.COLUMN_LAST_UPDATED);
+            final int indexVersion = c.getColumnIndex(IconDB.COLUMN_VERSION);
+            final int rowIndex = c.getColumnIndex(IconDB.COLUMN_ROWID);
+            final int systemStateIndex = c.getColumnIndex(IconDB.COLUMN_SYSTEM_STATE);
+
+            while (c.moveToNext()) {
+                String cn = c.getString(indexComponent);
+                ComponentName component = ComponentName.unflattenFromString(cn);
+                PackageInfo info = mPkgInfoMap.get(component.getPackageName());
+
+                int rowId = c.getInt(rowIndex);
+                if (info == null) {
+                    if (!ignorePackages.contains(component.getPackageName())) {
+
+                        if (mFilterMode == MODE_SET_INVALID_ITEMS) {
+                            mIconCache.remove(component, user);
+                            mItemsToDelete.put(rowId, true);
+                        }
+                    }
+                    continue;
+                }
+                if ((info.applicationInfo.flags & ApplicationInfo.FLAG_IS_DATA_ONLY) != 0) {
+                    // Application is not present
+                    continue;
+                }
+
+                long updateTime = c.getLong(indexLastUpdate);
+                int version = c.getInt(indexVersion);
+                T app = componentMap.remove(component);
+                if (version == info.versionCode && updateTime == info.lastUpdateTime &&
+                        TextUtils.equals(c.getString(systemStateIndex),
+                                mIconCache.getIconSystemState(info.packageName))) {
+
+                    if (mFilterMode == MODE_CLEAR_VALID_ITEMS) {
+                        mItemsToDelete.put(rowId, false);
+                    }
+                    continue;
+                }
+                if (app == null) {
+                    if (mFilterMode == MODE_SET_INVALID_ITEMS) {
+                        mIconCache.remove(component, user);
+                        mItemsToDelete.put(rowId, true);
+                    }
+                } else {
+                    appsToUpdate.add(app);
+                }
+            }
+        } catch (SQLiteException e) {
+            Log.d(TAG, "Error reading icon cache", e);
+            // Continue updating whatever we have read so far
+        }
+
+        // Insert remaining apps.
+        if (!componentMap.isEmpty() || !appsToUpdate.isEmpty()) {
+            Stack<T> appsToAdd = new Stack<>();
+            appsToAdd.addAll(componentMap.values());
+            new SerializedIconUpdateTask(userSerial, user, appsToAdd, appsToUpdate, cachingLogic,
+                    onUpdateCallback).scheduleNext();
+        }
+    }
+
+    /**
+     * Commits all updates as part of the update handler to disk. Not more calls should be made
+     * to this class after this.
+     */
+    public void finish() {
+        // Commit all deletes
+        int deleteCount = 0;
+        StringBuilder queryBuilder = new StringBuilder()
+                .append(IconDB.COLUMN_ROWID)
+                .append(" IN (");
+
+        int count = mItemsToDelete.size();
+        for (int i = 0;  i < count; i++) {
+            if (mItemsToDelete.valueAt(i)) {
+                if (deleteCount > 0) {
+                    queryBuilder.append(", ");
+                }
+                queryBuilder.append(mItemsToDelete.keyAt(i));
+                deleteCount++;
+            }
+        }
+        queryBuilder.append(')');
+
+        if (deleteCount > 0) {
+            mIconCache.mIconDb.delete(queryBuilder.toString(), null);
+        }
+    }
+
+
+    /**
+     * A runnable that updates invalid icons and adds missing icons in the DB for the provided
+     * LauncherActivityInfo list. Items are updated/added one at a time, so that the
+     * worker thread doesn't get blocked.
+     */
+    private class SerializedIconUpdateTask<T> implements Runnable {
+        private final long mUserSerial;
+        private final UserHandle mUserHandle;
+        private final Stack<T> mAppsToAdd;
+        private final Stack<T> mAppsToUpdate;
+        private final CachingLogic<T> mCachingLogic;
+        private final HashSet<String> mUpdatedPackages = new HashSet<>();
+        private final OnUpdateCallback mOnUpdateCallback;
+
+        SerializedIconUpdateTask(long userSerial, UserHandle userHandle,
+                Stack<T> appsToAdd, Stack<T> appsToUpdate, CachingLogic<T> cachingLogic,
+                OnUpdateCallback onUpdateCallback) {
+            mUserHandle = userHandle;
+            mUserSerial = userSerial;
+            mAppsToAdd = appsToAdd;
+            mAppsToUpdate = appsToUpdate;
+            mCachingLogic = cachingLogic;
+            mOnUpdateCallback = onUpdateCallback;
+        }
+
+        @Override
+        public void run() {
+            if (!mAppsToUpdate.isEmpty()) {
+                T app = mAppsToUpdate.pop();
+                String pkg = mCachingLogic.getComponent(app).getPackageName();
+                PackageInfo info = mPkgInfoMap.get(pkg);
+                mIconCache.addIconToDBAndMemCache(
+                        app, mCachingLogic, info, mUserSerial, true /*replace existing*/);
+                mUpdatedPackages.add(pkg);
+
+                if (mAppsToUpdate.isEmpty() && !mUpdatedPackages.isEmpty()) {
+                    // No more app to update. Notify callback.
+                    mOnUpdateCallback.onPackageIconsUpdated(mUpdatedPackages, mUserHandle);
+                }
+
+                // Let it run one more time.
+                scheduleNext();
+            } else if (!mAppsToAdd.isEmpty()) {
+                T app = mAppsToAdd.pop();
+                PackageInfo info = mPkgInfoMap.get(mCachingLogic.getComponent(app).getPackageName());
+                // We do not check the mPkgInfoMap when generating the mAppsToAdd. Although every
+                // app should have package info, this is not guaranteed by the api
+                if (info != null) {
+                    mIconCache.addIconToDBAndMemCache(app, mCachingLogic, info,
+                            mUserSerial, false /*replace existing*/);
+                }
+
+                if (!mAppsToAdd.isEmpty()) {
+                    scheduleNext();
+                }
+            }
+        }
+
+        public void scheduleNext() {
+            mIconCache.mWorkerHandler.postAtTime(this, ICON_UPDATE_TOKEN,
+                    SystemClock.uptimeMillis() + 1);
+        }
+    }
+
+    public interface OnUpdateCallback {
+
+        void onPackageIconsUpdated(HashSet<String> updatedPackages, UserHandle user);
+    }
+}
diff --git a/iconloaderlib/src/com/android/launcher3/util/ComponentKey.java b/iconloaderlib/src/com/android/launcher3/util/ComponentKey.java
new file mode 100644
index 0000000..34bed94
--- /dev/null
+++ b/iconloaderlib/src/com/android/launcher3/util/ComponentKey.java
@@ -0,0 +1,59 @@
+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 android.content.ComponentName;
+import android.os.UserHandle;
+
+import java.util.Arrays;
+
+public class ComponentKey {
+
+    public final ComponentName componentName;
+    public final UserHandle user;
+
+    private final int mHashCode;
+
+    public ComponentKey(ComponentName componentName, UserHandle user) {
+        if (componentName == null || user == null) {
+            throw new NullPointerException();
+        }
+        this.componentName = componentName;
+        this.user = user;
+        mHashCode = Arrays.hashCode(new Object[] {componentName, user});
+
+    }
+
+    @Override
+    public int hashCode() {
+        return mHashCode;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        ComponentKey other = (ComponentKey) o;
+        return other.componentName.equals(componentName) && other.user.equals(user);
+    }
+
+    /**
+     * Encodes a component key as a string of the form [flattenedComponentString#userId].
+     */
+    @Override
+    public String toString() {
+        return componentName.flattenToString() + "#" + user;
+    }
+}
\ No newline at end of file
diff --git a/iconloaderlib/src/com/android/launcher3/util/NoLocaleSQLiteHelper.java b/iconloaderlib/src/com/android/launcher3/util/NoLocaleSQLiteHelper.java
new file mode 100644
index 0000000..fe864a2
--- /dev/null
+++ b/iconloaderlib/src/com/android/launcher3/util/NoLocaleSQLiteHelper.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+import static android.database.sqlite.SQLiteDatabase.NO_LOCALIZED_COLLATORS;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.database.DatabaseErrorHandler;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteDatabase.CursorFactory;
+import android.database.sqlite.SQLiteDatabase.OpenParams;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.os.Build;
+
+/**
+ * Extension of {@link SQLiteOpenHelper} which avoids creating default locale table by
+ * A context wrapper which creates databases without support for localized collators.
+ */
+public abstract class NoLocaleSQLiteHelper extends SQLiteOpenHelper {
+
+    private static final boolean ATLEAST_P =
+            Build.VERSION.SDK_INT >= Build.VERSION_CODES.P;
+
+    public NoLocaleSQLiteHelper(Context context, String name, int version) {
+        super(ATLEAST_P ? context : new NoLocalContext(context), name, null, version);
+        if (ATLEAST_P) {
+            setOpenParams(new OpenParams.Builder().addOpenFlags(NO_LOCALIZED_COLLATORS).build());
+        }
+    }
+
+    private static class NoLocalContext extends ContextWrapper {
+        public NoLocalContext(Context base) {
+            super(base);
+        }
+
+        @Override
+        public SQLiteDatabase openOrCreateDatabase(
+                String name, int mode, CursorFactory factory, DatabaseErrorHandler errorHandler) {
+            return super.openOrCreateDatabase(
+                    name, mode | Context.MODE_NO_LOCALIZED_COLLATORS, factory, errorHandler);
+        }
+    }
+}
diff --git a/iconloaderlib/src/com/android/launcher3/util/SQLiteCacheHelper.java b/iconloaderlib/src/com/android/launcher3/util/SQLiteCacheHelper.java
new file mode 100644
index 0000000..49de4bd
--- /dev/null
+++ b/iconloaderlib/src/com/android/launcher3/util/SQLiteCacheHelper.java
@@ -0,0 +1,125 @@
+package com.android.launcher3.util;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteException;
+import android.database.sqlite.SQLiteFullException;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Log;
+
+/**
+ * An extension of {@link SQLiteOpenHelper} with utility methods for a single table cache DB.
+ * Any exception during write operations are ignored, and any version change causes a DB reset.
+ */
+public abstract class SQLiteCacheHelper {
+    private static final String TAG = "SQLiteCacheHelper";
+
+    private static final boolean IN_MEMORY_CACHE = false;
+
+    private final String mTableName;
+    private final MySQLiteOpenHelper mOpenHelper;
+
+    private boolean mIgnoreWrites;
+
+    public SQLiteCacheHelper(Context context, String name, int version, String tableName) {
+        if (IN_MEMORY_CACHE) {
+            name = null;
+        }
+        mTableName = tableName;
+        mOpenHelper = new MySQLiteOpenHelper(context, name, version);
+
+        mIgnoreWrites = false;
+    }
+
+    /**
+     * @see SQLiteDatabase#delete(String, String, String[])
+     */
+    public void delete(String whereClause, String[] whereArgs) {
+        if (mIgnoreWrites) {
+            return;
+        }
+        try {
+            mOpenHelper.getWritableDatabase().delete(mTableName, whereClause, whereArgs);
+        } catch (SQLiteFullException e) {
+            onDiskFull(e);
+        } catch (SQLiteException e) {
+            Log.d(TAG, "Ignoring sqlite exception", e);
+        }
+    }
+
+    /**
+     * @see SQLiteDatabase#insertWithOnConflict(String, String, ContentValues, int)
+     */
+    public void insertOrReplace(ContentValues values) {
+        if (mIgnoreWrites) {
+            return;
+        }
+        try {
+            mOpenHelper.getWritableDatabase().insertWithOnConflict(
+                    mTableName, null, values, SQLiteDatabase.CONFLICT_REPLACE);
+        } catch (SQLiteFullException e) {
+            onDiskFull(e);
+        } catch (SQLiteException e) {
+            Log.d(TAG, "Ignoring sqlite exception", e);
+        }
+    }
+
+    private void onDiskFull(SQLiteFullException e) {
+        Log.e(TAG, "Disk full, all write operations will be ignored", e);
+        mIgnoreWrites = true;
+    }
+
+    /**
+     * @see SQLiteDatabase#query(String, String[], String, String[], String, String, String)
+     */
+    public Cursor query(String[] columns, String selection, String[] selectionArgs) {
+        return mOpenHelper.getReadableDatabase().query(
+                mTableName, columns, selection, selectionArgs, null, null, null);
+    }
+
+    public void clear() {
+        mOpenHelper.clearDB(mOpenHelper.getWritableDatabase());
+    }
+
+    public void close() {
+        mOpenHelper.close();
+    }
+
+    protected abstract void onCreateTable(SQLiteDatabase db);
+
+    /**
+     * A private inner class to prevent direct DB access.
+     */
+    private class MySQLiteOpenHelper extends NoLocaleSQLiteHelper {
+
+        public MySQLiteOpenHelper(Context context, String name, int version) {
+            super(context, name, version);
+        }
+
+        @Override
+        public void onCreate(SQLiteDatabase db) {
+            onCreateTable(db);
+        }
+
+        @Override
+        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+            if (oldVersion != newVersion) {
+                clearDB(db);
+            }
+        }
+
+        @Override
+        public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+            if (oldVersion != newVersion) {
+                clearDB(db);
+            }
+        }
+
+        private void clearDB(SQLiteDatabase db) {
+            db.execSQL("DROP TABLE IF EXISTS " + mTableName);
+            onCreate(db);
+        }
+    }
+}
diff --git a/iconloaderlib/src_full_lib/com/android/launcher3/icons/IconFactory.java b/iconloaderlib/src_full_lib/com/android/launcher3/icons/IconFactory.java
new file mode 100644
index 0000000..48f11fd
--- /dev/null
+++ b/iconloaderlib/src_full_lib/com/android/launcher3/icons/IconFactory.java
@@ -0,0 +1,89 @@
+/*
+ * 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.icons;
+
+import android.content.Context;
+
+/**
+ * Wrapper class to provide access to {@link BaseIconFactory} and also to provide pool of this class
+ * that are threadsafe.
+ */
+public class IconFactory extends BaseIconFactory {
+
+    private static final Object sPoolSync = new Object();
+    private static IconFactory sPool;
+    private static int sPoolId = 0;
+
+    /**
+     * Return a new Message instance from the global pool. Allows us to
+     * avoid allocating new objects in many cases.
+     */
+    public static IconFactory obtain(Context context) {
+        int poolId;
+        synchronized (sPoolSync) {
+            if (sPool != null) {
+                IconFactory m = sPool;
+                sPool = m.next;
+                m.next = null;
+                return m;
+            }
+            poolId = sPoolId;
+        }
+
+        return new IconFactory(context,
+                context.getResources().getConfiguration().densityDpi,
+                context.getResources().getDimensionPixelSize(R.dimen.default_icon_bitmap_size),
+                poolId);
+    }
+
+    public static void clearPool() {
+        synchronized (sPoolSync) {
+            sPool = null;
+            sPoolId++;
+        }
+    }
+
+    private final int mPoolId;
+
+    private IconFactory next;
+
+    private IconFactory(Context context, int fillResIconDpi, int iconBitmapSize, int poolId) {
+        super(context, fillResIconDpi, iconBitmapSize);
+        mPoolId = poolId;
+    }
+
+    /**
+     * Recycles a LauncherIcons that may be in-use.
+     */
+    public void recycle() {
+        synchronized (sPoolSync) {
+            if (sPoolId != mPoolId) {
+                return;
+            }
+            // Clear any temporary state variables
+            clear();
+
+            next = sPool;
+            sPool = this;
+        }
+    }
+
+    @Override
+    public void close() {
+        recycle();
+    }
+}
diff --git a/iconloaderlib/src_full_lib/com/android/launcher3/icons/SimpleIconCache.java b/iconloaderlib/src_full_lib/com/android/launcher3/icons/SimpleIconCache.java
new file mode 100644
index 0000000..1337975
--- /dev/null
+++ b/iconloaderlib/src_full_lib/com/android/launcher3/icons/SimpleIconCache.java
@@ -0,0 +1,114 @@
+/*
+ * 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.icons;
+
+import static android.content.Intent.ACTION_MANAGED_PROFILE_ADDED;
+import static android.content.Intent.ACTION_MANAGED_PROFILE_REMOVED;
+
+import android.annotation.TargetApi;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.SparseLongArray;
+
+import com.android.launcher3.icons.cache.BaseIconCache;
+
+/**
+ * Wrapper class to provide access to {@link BaseIconFactory} and also to provide pool of this class
+ * that are threadsafe.
+ */
+@TargetApi(Build.VERSION_CODES.P)
+public class SimpleIconCache extends BaseIconCache {
+
+    private static SimpleIconCache sIconCache = null;
+    private static final Object CACHE_LOCK = new Object();
+
+    private final SparseLongArray mUserSerialMap = new SparseLongArray(2);
+    private final UserManager mUserManager;
+
+    public SimpleIconCache(Context context, String dbFileName, Looper bgLooper, int iconDpi,
+            int iconPixelSize, boolean inMemoryCache) {
+        super(context, dbFileName, bgLooper, iconDpi, iconPixelSize, inMemoryCache);
+        mUserManager = context.getSystemService(UserManager.class);
+
+        // Listen for user cache changes.
+        IntentFilter filter = new IntentFilter(ACTION_MANAGED_PROFILE_ADDED);
+        filter.addAction(ACTION_MANAGED_PROFILE_REMOVED);
+        context.registerReceiver(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                resetUserCache();
+            }
+        }, filter, null, new Handler(bgLooper), 0);
+    }
+
+    @Override
+    protected long getSerialNumberForUser(UserHandle user) {
+        synchronized (mUserSerialMap) {
+            int index = mUserSerialMap.indexOfKey(user.getIdentifier());
+            if (index >= 0) {
+                return mUserSerialMap.valueAt(index);
+            }
+            long serial = mUserManager.getSerialNumberForUser(user);
+            mUserSerialMap.put(user.getIdentifier(), serial);
+            return serial;
+        }
+    }
+
+    private void resetUserCache() {
+        synchronized (mUserSerialMap) {
+            mUserSerialMap.clear();
+        }
+    }
+
+    @Override
+    protected boolean isInstantApp(ApplicationInfo info) {
+        return info.isInstantApp();
+    }
+
+    @Override
+    protected BaseIconFactory getIconFactory() {
+        return IconFactory.obtain(mContext);
+    }
+
+    public static SimpleIconCache getIconCache(Context context) {
+        synchronized (CACHE_LOCK) {
+            if (sIconCache != null) {
+                return sIconCache;
+            }
+            boolean inMemoryCache =
+                    context.getResources().getBoolean(R.bool.simple_cache_enable_im_memory);
+            String dbFileName = context.getString(R.string.cache_db_name);
+
+            HandlerThread bgThread = new HandlerThread("simple-icon-cache");
+            bgThread.start();
+
+            sIconCache = new SimpleIconCache(context.getApplicationContext(), dbFileName,
+                    bgThread.getLooper(), context.getResources().getConfiguration().densityDpi,
+                    context.getResources().getDimensionPixelSize(R.dimen.default_icon_bitmap_size),
+                    inMemoryCache);
+            return sIconCache;
+        }
+    }
+}
diff --git a/proguard.flags b/proguard.flags
index e401116..272ab7a 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -2,80 +2,6 @@
   *;
 }
 
--keep class com.android.launcher3.allapps.AllAppsBackgroundDrawable {
-  public void setAlpha(int);
-  public int getAlpha();
-}
-
--keep class com.android.launcher3.BaseRecyclerViewFastScrollBar {
-  public void setThumbWidth(int);
-  public int getThumbWidth();
-  public void setTrackWidth(int);
-  public int getTrackWidth();
-}
-
--keep class com.android.launcher3.BaseRecyclerViewFastScrollPopup {
-  public void setAlpha(float);
-  public float getAlpha();
-}
-
--keep class com.android.launcher3.ButtonDropTarget {
-  public int getTextColor();
-}
-
--keep class com.android.launcher3.CellLayout {
-  public float getBackgroundAlpha();
-  public void setBackgroundAlpha(float);
-}
-
--keep class com.android.launcher3.CellLayout$LayoutParams {
-  public void setWidth(int);
-  public int getWidth();
-  public void setHeight(int);
-  public int getHeight();
-  public void setX(int);
-  public int getX();
-  public void setY(int);
-  public int getY();
-}
-
--keep class com.android.launcher3.views.BaseDragLayer$LayoutParams {
-  public void setWidth(int);
-  public int getWidth();
-  public void setHeight(int);
-  public int getHeight();
-  public void setX(int);
-  public int getX();
-  public void setY(int);
-  public int getY();
-}
-
--keep class com.android.launcher3.FastBitmapDrawable {
-  public void setDesaturation(float);
-  public float getDesaturation();
-  public void setBrightness(float);
-  public float getBrightness();
-}
-
--keep class com.android.launcher3.MemoryDumpActivity {
-  *;
-}
-
--keep class com.android.launcher3.PreloadIconDrawable {
-  public float getAnimationProgress();
-  public void setAnimationProgress(float);
-}
-
--keep class com.android.launcher3.pageindicators.CaretDrawable {
-  public float getCaretProgress();
-  public void setCaretProgress(float);
-}
-
--keep class com.android.launcher3.Workspace {
-  public float getBackgroundAlpha();
-  public void setBackgroundAlpha(float);
-}
-
 # Proguard will strip new callbacks in LauncherApps.Callback from
 # WrappedCallback if compiled against an older SDK. Don't let this happen.
 -keep class com.android.launcher3.compat.** {
@@ -95,32 +21,21 @@
 # next row when focus is on the last item of last row when using a RecyclerView
 # Keep optimized and shrunk proguard to prevent issues like this when using
 # support jar.
--keep class android.support.v7.widget.RecyclerView { *; }
+-keep class androidx.recyclerview.widget.RecyclerView { *; }
 
-# LauncherAppTransitionManager
--keep class com.android.launcher3.LauncherAppTransitionManagerImpl {
+# Preference fragments
+-keep class ** extends android.app.Fragment {
     public <init>(...);
 }
 
-# InstantAppResolver
--keep class com.android.quickstep.InstantAppResolverImpl {
-    public <init>(...);
-}
-
-# MainProcessInitializer
--keep class com.android.quickstep.QuickstepProcessInitializer {
-    public <init>(...);
-}
-
-# UserEventDispatcherExtension
--keep class com.android.quickstep.logging.UserEventDispatcherExtension {
+## Prevent obfuscating various overridable objects
+-keep class ** implements com.android.launcher3.util.ResourceBasedOverride {
     public <init>(...);
 }
 
 -keep interface com.android.launcher3.userevent.nano.LauncherLogProto.** {
   *;
 }
-
 -keep interface com.android.launcher3.model.nano.LauncherDumpProto.** {
   *;
 }
@@ -135,3 +50,4 @@
 -dontwarn android.app.**
 -dontwarn android.view.**
 -dontwarn android.os.**
+-dontwarn android.graphics.**
\ No newline at end of file
diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto
index 06e6a92..49fd436 100644
--- a/protos/launcher_log.proto
+++ b/protos/launcher_log.proto
@@ -56,6 +56,7 @@
   optional int32 predictedRank = 15;
   optional TargetExtension extension = 16;
   optional TipType tip_type = 17;
+  optional int32 search_query_length = 18;
 }
 
 // Used to define what type of item a Target would represent.
@@ -71,6 +72,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.
@@ -106,12 +108,16 @@
   RESIZE_HANDLE = 8;
   VERTICAL_SCROLL = 9;
   HOME_INTENT = 10; // Deprecated, use enum Command instead
-  BACK_BUTTON = 11; // 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;
 }
 
 enum TipType {
@@ -120,6 +126,7 @@
   SWIPE_UP_TEXT = 2;
   QUICK_SCRUB_TEXT = 3;
   PREDICTION_TEXT = 4;
+  DWB_TOAST = 5;
 }
 
 // Used to define the action component of the LauncherEvent.
@@ -139,6 +146,7 @@
     SWIPE = 3;
     FLING = 4;
     PINCH = 5;
+    SWIPE_NOOP = 6;
   }
 
   enum Direction {
@@ -147,6 +155,8 @@
     DOWN = 2;
     LEFT = 3;
     RIGHT = 4;
+    UPRIGHT = 5;
+    UPLEFT = 6;
   }
   enum Command {
     HOME_INTENT = 0;
@@ -184,8 +194,8 @@
   optional int64 elapsed_container_millis = 5;
   optional int64 elapsed_session_millis = 6;
 
-  optional bool is_in_multi_window_mode = 7;
-  optional bool is_in_landscape_mode = 8;
+  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/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index cb74855..332e0fa 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -22,8 +22,8 @@
     xmlns:tools="http://schemas.android.com/tools"
     package="com.android.launcher3" >
 
-    <uses-sdk android:targetSdkVersion="28" android:minSdkVersion="28"/>
     <uses-permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS" />
+
     <application
         android:backupAgent="com.android.launcher3.LauncherBackupAgent"
         android:fullBackupOnly="true"
@@ -38,7 +38,8 @@
 
         <service
             android:name="com.android.quickstep.TouchInteractionService"
-            android:permission="android.permission.STATUS_BAR_SERVICE" >
+            android:permission="android.permission.STATUS_BAR_SERVICE"
+            android:directBootAware="true" >
             <intent-filter>
                 <action android:name="android.intent.action.QUICKSTEP_SERVICE" />
             </intent-filter>
@@ -59,10 +60,10 @@
             android:resumeWhilePausing="true"
             android:taskAffinity="" />
 
-        <!-- Content provider to settings search -->
+        <!-- Content provider to settings search. The autority should be same as the packageName -->
         <provider
             android:name="com.android.quickstep.LauncherSearchIndexablesProvider"
-            android:authorities="com.android.launcher3"
+            android:authorities="${packageName}"
             android:grantUriPermissions="true"
             android:multiprocess="true"
             android:permission="android.permission.READ_SEARCH_INDEXABLES"
@@ -72,11 +73,22 @@
             </intent-filter>
         </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.quickstep.LockScreenRecentsActivity"
+                  android:theme="@android:style/Theme.NoDisplay"
+                  android:showOnLockScreen="true"
+                  android:directBootAware="true" />
+
     </application>
 
 </manifest>
diff --git a/quickstep/libs/sysui_shared.jar b/quickstep/libs/sysui_shared.jar
deleted file mode 100644
index 27de1e9..0000000
--- a/quickstep/libs/sysui_shared.jar
+++ /dev/null
Binary files differ
diff --git a/quickstep/recents_ui_overrides/res/drawable/arrow_toast_rounded_background.xml b/quickstep/recents_ui_overrides/res/drawable/arrow_toast_rounded_background.xml
new file mode 100644
index 0000000..52cc6fc
--- /dev/null
+++ b/quickstep/recents_ui_overrides/res/drawable/arrow_toast_rounded_background.xml
@@ -0,0 +1,19 @@
+<!--
+    Copyright (C) 2018 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+    <solid android:color="?android:attr/colorAccent" />
+    <corners android:radius="8dp" />
+</shape>
diff --git a/quickstep/recents_ui_overrides/res/drawable/chip_hint_background_light.xml b/quickstep/recents_ui_overrides/res/drawable/chip_hint_background_light.xml
new file mode 100644
index 0000000..7b4da83
--- /dev/null
+++ b/quickstep/recents_ui_overrides/res/drawable/chip_hint_background_light.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+    <stroke
+        android:color="@color/chip_hint_foreground_color"
+        android:width="@dimen/chip_hint_border_width"/>
+    <corners android:radius="@dimen/chip_hint_corner_radius"/>
+    <padding
+        android:left="@dimen/chip_hint_outer_padding"
+        android:top="@dimen/chip_hint_outer_padding"
+        android:right="@dimen/chip_hint_outer_padding"
+        android:bottom="@dimen/chip_hint_outer_padding"/>
+</shape>
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/res/layout/arrow_toast.xml b/quickstep/recents_ui_overrides/res/layout/arrow_toast.xml
new file mode 100644
index 0000000..b0f2b4b
--- /dev/null
+++ b/quickstep/recents_ui_overrides/res/layout/arrow_toast.xml
@@ -0,0 +1,62 @@
+<?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.
+-->
+
+<merge
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="wrap_content"
+    android:layout_width="wrap_content">
+
+    <LinearLayout
+        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:elevation="2dp"
+        android:orientation="horizontal">
+
+        <TextView
+            android:id="@+id/text"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            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_task"/>
+    </LinearLayout>
+
+    <View
+        android:id="@+id/arrow"
+        android:elevation="2dp"
+        android:layout_width="10dp"
+        android:layout_height="8dp"
+        android:layout_marginTop="-2dp"
+        android:layout_gravity="center_horizontal"/>
+</merge>
diff --git a/quickstep/res/layout/fallback_recents_activity.xml b/quickstep/recents_ui_overrides/res/layout/fallback_recents_activity.xml
similarity index 100%
rename from quickstep/res/layout/fallback_recents_activity.xml
rename to quickstep/recents_ui_overrides/res/layout/fallback_recents_activity.xml
diff --git a/quickstep/recents_ui_overrides/res/layout/floating_header_content.xml b/quickstep/recents_ui_overrides/res/layout/floating_header_content.xml
new file mode 100644
index 0000000..b21c34b
--- /dev/null
+++ b/quickstep/recents_ui_overrides/res/layout/floating_header_content.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<merge xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <com.android.launcher3.appprediction.PredictionRowView
+        android:id="@+id/prediction_row"
+        android:accessibilityPaneTitle="@string/title_app_suggestions"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+    <com.android.launcher3.appprediction.AppsDividerView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:id="@+id/apps_divider_view" />
+</merge>
diff --git a/quickstep/res/layout/overview_panel.xml b/quickstep/recents_ui_overrides/res/layout/overview_panel.xml
similarity index 100%
rename from quickstep/res/layout/overview_panel.xml
rename to quickstep/recents_ui_overrides/res/layout/overview_panel.xml
diff --git a/quickstep/recents_ui_overrides/res/values/colors.xml b/quickstep/recents_ui_overrides/res/values/colors.xml
new file mode 100644
index 0000000..7426e30
--- /dev/null
+++ b/quickstep/recents_ui_overrides/res/values/colors.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="chip_hint_foreground_color">#fff</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
new file mode 100644
index 0000000..863a8ba
--- /dev/null
+++ b/quickstep/recents_ui_overrides/res/values/dimens.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<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_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>
+    <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>
+
+    <!-- Minimum distance to swipe to trigger accessibility gesture -->
+    <dimen name="accessibility_gesture_min_swipe_distance">80dp</dimen>
+
+    <!-- Swipe up to home related -->
+    <dimen name="swipe_up_fling_min_visible_change">18dp</dimen>
+    <dimen name="swipe_up_y_overshoot">10dp</dimen>
+    <dimen name="swipe_up_max_workspace_trans_y">-60dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/res/values/override.xml b/quickstep/recents_ui_overrides/res/values/override.xml
new file mode 100644
index 0000000..1ddd3f5
--- /dev/null
+++ b/quickstep/recents_ui_overrides/res/values/override.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+
+<!-- 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>
+</resources>
+
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
new file mode 100644
index 0000000..371161e
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -0,0 +1,165 @@
+/*
+ * 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.LauncherAnimUtils.VIEW_TRANSLATE_X;
+import static com.android.launcher3.LauncherState.NORMAL;
+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.LINEAR;
+import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
+import static com.android.quickstep.TaskViewUtils.getRecentsWindowAnimator;
+
+import static androidx.dynamicanimation.animation.DynamicAnimation.MIN_VISIBLE_CHANGE_PIXELS;
+import static androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY;
+import static androidx.dynamicanimation.animation.SpringForce.STIFFNESS_MEDIUM;
+
+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.allapps.AllAppsTransitionController;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.anim.SpringObjectAnimator;
+import com.android.quickstep.util.ClipAnimationHelper;
+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 static final int INDEX_SHELF_ANIM = 0;
+    public static final int INDEX_RECENTS_FADE_ANIM = 1;
+    public static final int INDEX_RECENTS_TRANSLATE_X_ANIM = 2;
+
+    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[] targets, boolean launcherClosing) {
+        RecentsView recentsView = mLauncher.getOverviewPanel();
+        boolean skipLauncherChanges = !launcherClosing;
+
+        TaskView taskView = findTaskViewToLaunch(mLauncher, v, targets);
+
+        ClipAnimationHelper helper = new ClipAnimationHelper(mLauncher);
+        anim.play(getRecentsWindowAnimator(taskView, skipLauncherChanges, targets, helper)
+                .setDuration(RECENTS_LAUNCH_DURATION));
+
+        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, helper);
+            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);
+            mLauncher.getStateManager().reapplyState();
+        };
+    }
+
+    @Override
+    public int getStateElementAnimationsCount() {
+        return 3;
+    }
+
+    @Override
+    public Animator createStateElementAnimation(int index, float... values) {
+        switch (index) {
+            case INDEX_SHELF_ANIM:
+                return mLauncher.getAllAppsController().createSpringAnimation(values);
+            case INDEX_RECENTS_FADE_ANIM:
+                return ObjectAnimator.ofFloat(mLauncher.getOverviewPanel(),
+                        RecentsView.CONTENT_ALPHA, values);
+            case INDEX_RECENTS_TRANSLATE_X_ANIM:
+                return new SpringObjectAnimator<>(mLauncher.getOverviewPanel(),
+                        VIEW_TRANSLATE_X, MIN_VISIBLE_CHANGE_PIXELS, 0.8f, 250, values);
+            default:
+                return super.createStateElementAnimation(index, values);
+        }
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherInitListenerEx.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherInitListenerEx.java
new file mode 100644
index 0000000..c5c4add
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherInitListenerEx.java
@@ -0,0 +1,34 @@
+/*
+ * 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;
+
+import com.android.launcher3.appprediction.PredictionUiStateManager;
+import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
+
+import java.util.function.BiPredicate;
+
+public class LauncherInitListenerEx extends LauncherInitListener {
+
+    public LauncherInitListenerEx(BiPredicate<Launcher, Boolean> onInitListener) {
+        super(onInitListener);
+    }
+
+    @Override
+    protected boolean init(Launcher launcher, boolean alreadyOnHome) {
+        PredictionUiStateManager.INSTANCE.get(launcher).switchClient(Client.OVERVIEW);
+        return super.init(launcher, alreadyOnHome);
+    }
+}
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
new file mode 100644
index 0000000..d3042cf
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java
@@ -0,0 +1,207 @@
+/**
+ * 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.LauncherState.ALL_APPS;
+import static com.android.quickstep.logging.UserEventDispatcherExtension.ALL_APPS_PREDICTION_TIPS;
+
+import android.content.Context;
+import android.graphics.CornerPathEffect;
+import android.graphics.Paint;
+import android.graphics.drawable.ShapeDrawable;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.allapps.FloatingHeaderView;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.graphics.TriangleShape;
+import com.android.systemui.shared.system.LauncherEventUtil;
+
+import androidx.core.content.ContextCompat;
+
+/**
+ * All apps tip view aligned just above prediction apps, shown to users that enter all apps for the
+ * first time.
+ */
+public class AllAppsTipView extends AbstractFloatingView {
+
+    private static final String ALL_APPS_TIP_SEEN = "launcher.all_apps_tip_seen";
+    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;
+    private static final long HIDE_DURATION_MS = 100;
+
+    private final Launcher mLauncher;
+    private final Handler mHandler = new Handler();
+
+    private AllAppsTipView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    private AllAppsTipView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        setOrientation(LinearLayout.VERTICAL);
+
+        mLauncher = Launcher.getLauncher(context);
+
+        init(context);
+    }
+
+    @Override
+    public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+            close(true);
+        }
+        return false;
+    }
+
+    @Override
+    protected void handleClose(boolean animate) {
+        if (mIsOpen) {
+            if (animate) {
+                animate().alpha(0f)
+                        .withLayer()
+                        .setStartDelay(0)
+                        .setDuration(HIDE_DURATION_MS)
+                        .setInterpolator(Interpolators.ACCEL)
+                        .withEndAction(() -> mLauncher.getDragLayer().removeView(this))
+                        .start();
+            } else {
+                animate().cancel();
+                mLauncher.getDragLayer().removeView(this);
+            }
+            mLauncher.getSharedPrefs().edit().putBoolean(ALL_APPS_TIP_SEEN, true).apply();
+            mIsOpen = false;
+        }
+    }
+
+    @Override
+    public void logActionCommand(int command) {
+    }
+
+    @Override
+    protected boolean isOfType(int type) {
+        return (type & TYPE_ON_BOARD_POPUP) != 0;
+    }
+
+    private void init(Context context) {
+        inflate(context, R.layout.arrow_toast, this);
+
+        TextView textView = findViewById(R.id.text);
+        textView.setText(R.string.all_apps_prediction_tip);
+
+        View dismissButton = findViewById(R.id.dismiss);
+        dismissButton.setOnClickListener(view -> {
+            mLauncher.getUserEventDispatcher().logActionTip(
+                    LauncherEventUtil.DISMISS, ALL_APPS_PREDICTION_TIPS);
+            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);
+    }
+
+    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)
+                || UserManagerCompat.getInstance(launcher).isDemoUser()
+                || Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+            return false;
+        }
+
+        AllAppsTipView allAppsTipView = new AllAppsTipView(launcher.getAppsView().getContext(),
+            null);
+        launcher.getDragLayer().addView(allAppsTipView);
+
+        DragLayer.LayoutParams params = (DragLayer.LayoutParams) allAppsTipView.getLayoutParams();
+        params.gravity = Gravity.CENTER_HORIZONTAL;
+
+        int top = floatingHeaderView.findFixedRowByType(PredictionRowView.class).getTop();
+        allAppsTipView.setY(top - launcher.getResources().getDimensionPixelSize(
+                R.dimen.all_apps_tip_bottom_margin));
+
+        allAppsTipView.setAlpha(0);
+        allAppsTipView.animate()
+                .alpha(1f)
+                .withLayer()
+                .setStartDelay(SHOW_DELAY_MS)
+                .setDuration(SHOW_DURATION_MS)
+                .setInterpolator(Interpolators.DEACCEL)
+                .start();
+
+        launcher.getUserEventDispatcher().logActionTip(
+                LauncherEventUtil.VISIBLE, ALL_APPS_PREDICTION_TIPS);
+        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 LauncherStateManager.StateListener() {
+                        @Override
+                        public void onStateTransitionStart(LauncherState toState) {
+                        }
+
+                        @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/AppsDividerView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AppsDividerView.java
new file mode 100644
index 0000000..311db21
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AppsDividerView.java
@@ -0,0 +1,308 @@
+/**
+ * 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.LauncherState.ALL_APPS;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.os.Build;
+import android.text.Layout;
+import android.text.StaticLayout;
+import android.text.TextPaint;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.animation.Interpolator;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager;
+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.util.Themes;
+
+import androidx.annotation.ColorInt;
+import androidx.core.content.ContextCompat;
+
+/**
+ * A view which shows a horizontal divider
+ */
+@TargetApi(Build.VERSION_CODES.O)
+public class AppsDividerView extends View implements LauncherStateManager.StateListener,
+        FloatingHeaderRow {
+
+    private static final String ALL_APPS_VISITED_COUNT = "launcher.all_apps_visited_count";
+    private static final int SHOW_ALL_APPS_LABEL_ON_ALL_APPS_VISITED_COUNT = 20;
+
+    public enum DividerType {
+        NONE,
+        LINE,
+        ALL_APPS_LABEL
+    }
+
+    private final Launcher mLauncher;
+    private final TextPaint mPaint = new TextPaint();
+    private DividerType mDividerType = DividerType.NONE;
+
+    private final @ColorInt int mStrokeColor;
+    private final @ColorInt int mAllAppsLabelTextColor;
+
+    private Layout mAllAppsLabelLayout;
+    private boolean mShowAllAppsLabel;
+
+    private FloatingHeaderView mParent;
+    private boolean mTabsHidden;
+    private FloatingHeaderRow[] mRows = FloatingHeaderRow.NO_ROWS;
+
+    private boolean mIsScrolledOut = false;
+
+    public AppsDividerView(Context context) {
+        this(context, null);
+    }
+
+    public AppsDividerView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public AppsDividerView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        mLauncher = Launcher.getLauncher(context);
+
+        boolean isMainColorDark = Themes.getAttrBoolean(context, R.attr.isMainColorDark);
+        mPaint.setStrokeWidth(getResources().getDimensionPixelSize(R.dimen.all_apps_divider_height));
+
+        mStrokeColor = ContextCompat.getColor(context, isMainColorDark
+                ? R.color.all_apps_prediction_row_separator_dark
+                : R.color.all_apps_prediction_row_separator);
+
+        mAllAppsLabelTextColor = ContextCompat.getColor(context, isMainColorDark
+                ? R.color.all_apps_label_text_dark
+                : R.color.all_apps_label_text);
+    }
+
+    public void setup(FloatingHeaderView parent, FloatingHeaderRow[] rows, boolean tabsHidden) {
+        mParent = parent;
+        mTabsHidden = tabsHidden;
+        mRows = rows;
+        updateDividerType();
+    }
+
+    @Override
+    public int getExpectedHeight() {
+        return getPaddingTop() + getPaddingBottom();
+    }
+
+    @Override
+    public boolean shouldDraw() {
+        return mDividerType != DividerType.NONE;
+    }
+
+    @Override
+    public boolean hasVisibleContent() {
+        return false;
+    }
+
+    private void updateDividerType() {
+        final DividerType dividerType;
+        if (!mTabsHidden) {
+            dividerType = DividerType.NONE;
+        } else {
+            // Check how many sections above me.
+            int sectionCount = 0;
+            for (FloatingHeaderRow row : mRows) {
+                if (row == this) {
+                    break;
+                } else if (row.shouldDraw()) {
+                    sectionCount ++;
+                }
+            }
+
+            if (mShowAllAppsLabel && sectionCount > 0) {
+                dividerType = DividerType.ALL_APPS_LABEL;
+            } else if (sectionCount == 1) {
+                dividerType = DividerType.LINE;
+            } else {
+                dividerType = DividerType.NONE;
+            }
+        }
+
+        if (mDividerType != dividerType) {
+            mDividerType = dividerType;
+            int topPadding;
+            int bottomPadding;
+            switch (dividerType) {
+                case LINE:
+                    topPadding = 0;
+                    bottomPadding = getResources()
+                            .getDimensionPixelSize(R.dimen.all_apps_prediction_row_divider_height);
+                    mPaint.setColor(mStrokeColor);
+                    break;
+                case ALL_APPS_LABEL:
+                    topPadding = getAllAppsLabelLayout().getHeight() + getResources()
+                            .getDimensionPixelSize(R.dimen.all_apps_label_top_padding);
+                    bottomPadding = getResources()
+                            .getDimensionPixelSize(R.dimen.all_apps_label_bottom_padding);
+                    mPaint.setColor(mAllAppsLabelTextColor);
+                    break;
+                case NONE:
+                default:
+                    topPadding = bottomPadding = 0;
+                    break;
+            }
+            setPadding(getPaddingLeft(), topPadding, getPaddingRight(), bottomPadding);
+            updateViewVisibility();
+            invalidate();
+            requestLayout();
+            if (mParent != null) {
+                mParent.onHeightUpdated();
+            }
+        }
+    }
+
+    private void updateViewVisibility() {
+        setVisibility(mDividerType == DividerType.NONE
+                ? GONE
+                : (mIsScrolledOut ? INVISIBLE : VISIBLE));
+    }
+
+    @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);
+        } else if (mDividerType == DividerType.ALL_APPS_LABEL) {
+            Layout textLayout = getAllAppsLabelLayout();
+            int x = getWidth() / 2 - textLayout.getWidth() / 2;
+            int y = getHeight() - getPaddingBottom() - textLayout.getHeight();
+            canvas.translate(x, y);
+            textLayout.draw(canvas);
+            canvas.translate(-x, -y);
+        }
+    }
+
+    private Layout getAllAppsLabelLayout() {
+        if (mAllAppsLabelLayout == null) {
+            mPaint.setAntiAlias(true);
+            mPaint.setTypeface(Typeface.create("sans-serif-medium", Typeface.NORMAL));
+            mPaint.setTextSize(
+                    getResources().getDimensionPixelSize(R.dimen.all_apps_label_text_size));
+
+            CharSequence allAppsLabelText = getResources().getText(R.string.all_apps_label);
+            mAllAppsLabelLayout = StaticLayout.Builder.obtain(
+                    allAppsLabelText, 0, allAppsLabelText.length(), mPaint,
+                    Math.round(mPaint.measureText(allAppsLabelText.toString())))
+                    .setAlignment(Layout.Alignment.ALIGN_CENTER)
+                    .setMaxLines(1)
+                    .setIncludePad(true)
+                    .build();
+        }
+        return mAllAppsLabelLayout;
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
+
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
+                getPaddingBottom() + getPaddingTop());
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
+        if (shouldShowAllAppsLabel()) {
+            mShowAllAppsLabel = true;
+            mLauncher.getStateManager().addStateListener(this);
+            updateDividerType();
+        }
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        mLauncher.getStateManager().removeStateListener(this);
+    }
+
+    @Override
+    public void onStateTransitionStart(LauncherState toState) { }
+
+    @Override
+    public void onStateTransitionComplete(LauncherState finalState) {
+        if (finalState == ALL_APPS) {
+            setAllAppsVisitedCount(getAllAppsVisitedCount() + 1);
+        } else {
+            if (mShowAllAppsLabel != shouldShowAllAppsLabel()) {
+                mShowAllAppsLabel = !mShowAllAppsLabel;
+                updateDividerType();
+            }
+
+            if (!mShowAllAppsLabel) {
+                mLauncher.getStateManager().removeStateListener(this);
+            }
+        }
+    }
+
+    private void setAllAppsVisitedCount(int count) {
+        mLauncher.getSharedPrefs().edit().putInt(ALL_APPS_VISITED_COUNT, count).apply();
+    }
+
+    private int getAllAppsVisitedCount() {
+        return mLauncher.getSharedPrefs().getInt(ALL_APPS_VISITED_COUNT, 0);
+    }
+
+    private boolean shouldShowAllAppsLabel() {
+        return getAllAppsVisitedCount() < SHOW_ALL_APPS_LABEL_ON_ALL_APPS_VISITED_COUNT;
+    }
+
+    @Override
+    public void setInsets(Rect insets, DeviceProfile grid) {
+        int leftRightPadding = grid.desiredWorkspaceLeftRightMarginPx
+                + grid.cellLayoutPaddingLeftRightPx;
+        setPadding(leftRightPadding, getPaddingTop(), leftRightPadding, getPaddingBottom());
+    }
+
+    @Override
+    public void setContentVisibility(boolean hasHeaderExtra, boolean hasContent,
+            PropertySetter setter, Interpolator fadeInterpolator) {
+        // Don't use setViewAlpha as we want to control the visibility ourselves.
+        setter.setFloat(this, ALPHA, hasContent ? 1 : 0, fadeInterpolator);
+    }
+
+    @Override
+    public void setVerticalScroll(int scroll, boolean isScrolledOut) {
+        setTranslationY(scroll);
+        mIsScrolledOut = isScrolledOut;
+        updateViewVisibility();
+    }
+
+    @Override
+    public Class<AppsDividerView> getTypeClass() {
+        return AppsDividerView.class;
+    }
+}
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
new file mode 100644
index 0000000..b9f4147
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/ComponentKeyMapper.java
@@ -0,0 +1,69 @@
+/**
+ * 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 android.content.Context;
+
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.ItemInfoWithIcon;
+import com.android.launcher3.allapps.AllAppsStore;
+import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.util.ComponentKey;
+
+public class ComponentKeyMapper {
+
+    protected final ComponentKey componentKey;
+    private final Context mContext;
+    private final DynamicItemCache mCache;
+
+    public ComponentKeyMapper(Context context, ComponentKey key, DynamicItemCache cache) {
+        mContext = context;
+        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 if (componentKey instanceof ShortcutKey) {
+            return mCache.getShortcutInfo((ShortcutKey) componentKey);
+        }
+        return null;
+    }
+}
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
new file mode 100644
index 0000000..4ecc39c
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java
@@ -0,0 +1,242 @@
+/**
+ * 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 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 com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.shortcuts.DeepShortcutManager;
+import com.android.launcher3.shortcuts.ShortcutKey;
+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;
+
+import androidx.annotation.MainThread;
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+import androidx.annotation.WorkerThread;
+
+/**
+ * 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 Map<ShortcutKey, WorkspaceItemInfo> mShortcuts;
+    private final Map<String, InstantAppItemInfo> mInstantApps;
+
+    public DynamicItemCache(Context context, Runnable onUpdateCallback) {
+        mContext = context;
+        mWorker = new Handler(LauncherModel.getWorkerLooper(), this::handleWorkerMessage);
+        mUiHandler = new Handler(Looper.getMainLooper(), this::handleUiMessage);
+        mInstantAppResolver = InstantAppResolver.newInstance(context);
+        mOnUpdateCallback = onUpdateCallback;
+
+        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) {
+        DeepShortcutManager mgr = DeepShortcutManager.getInstance(mContext);
+        List<ShortcutInfo> details = mgr.queryForFullDetails(
+                shortcutKey.componentName.getPackageName(),
+                Collections.<String>singletonList(shortcutKey.getId()),
+                shortcutKey.user);
+        if (!details.isEmpty()) {
+            WorkspaceItemInfo si = new WorkspaceItemInfo(details.get(0), mContext);
+            try (LauncherIcons li = LauncherIcons.obtain(mContext)) {
+                si.applyFrom(li.createShortcutIcon(details.get(0), true /* badged */, null));
+            } catch (Exception e) {
+                if (DEBUG) {
+                    Log.e(TAG, "Error loading shortcut icon for " + shortcutKey.toString());
+                }
+                return null;
+            }
+            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.iconBitmap == null || iconCache.isDefaultIcon(info.iconBitmap, 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(ShortcutKey key) {
+        return mShortcuts.get(key);
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/InstantAppItemInfo.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/InstantAppItemInfo.java
new file mode 100644
index 0000000..6e5f461
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/InstantAppItemInfo.java
@@ -0,0 +1,50 @@
+/**
+ * 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 android.content.ComponentName;
+import android.content.Intent;
+
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.WorkspaceItemInfo;
+
+public class InstantAppItemInfo extends AppInfo {
+
+    public InstantAppItemInfo(Intent intent, String packageName) {
+        this.intent = intent;
+        this.componentName = new ComponentName(packageName, COMPONENT_CLASS_MARKER);
+    }
+
+    @Override
+    public ComponentName getTargetComponent() {
+        return componentName;
+    }
+
+    @Override
+    public WorkspaceItemInfo makeWorkspaceItem() {
+        WorkspaceItemInfo workspaceItemInfo = super.makeWorkspaceItem();
+        workspaceItemInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+        workspaceItemInfo.status = WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON
+                | WorkspaceItemInfo.FLAG_RESTORE_STARTED
+                | WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI;
+        workspaceItemInfo.intent.setPackage(componentName.getPackageName());
+        return workspaceItemInfo;
+    }
+}
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
new file mode 100644
index 0000000..8f1282d
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java
@@ -0,0 +1,199 @@
+/**
+ * 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 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 com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
+import com.android.launcher3.model.AppLaunchTracker;
+import com.android.launcher3.util.UiThreadHelper;
+
+import androidx.annotation.UiThread;
+import androidx.annotation.WorkerThread;
+
+/**
+ * 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 {
+
+    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;
+
+    // Accessed only on worker thread
+    private AppPredictor mHomeAppPredictor;
+    private AppPredictor mRecentsOverviewPredictor;
+
+    public PredictionAppTracker(Context context) {
+        mContext = context;
+        mMessageHandler = new Handler(UiThreadHelper.getBackgroundLooper(), this::handleMessage);
+        InvariantDeviceProfile.INSTANCE.get(mContext).addOnChangeListener(this::onIdpChanged);
+
+        mMessageHandler.sendEmptyMessage(MSG_INIT);
+    }
+
+    @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);
+
+        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).numColumns;
+                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));
+        }
+    }
+
+    @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);
+    }
+
+    @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);
+        }
+    }
+
+    @UiThread
+    private void sendLaunch(AppTarget target, String container) {
+        AppTargetEvent event = new AppTargetEvent.Builder(target, AppTargetEvent.ACTION_LAUNCH)
+                .setLaunchLocation(container == null ? CONTAINER_DEFAULT : container)
+                .build();
+        Message.obtain(mMessageHandler, MSG_LAUNCH, event).sendToTarget();
+    }
+}
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
new file mode 100644
index 0000000..cb5cbdd
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -0,0 +1,384 @@
+/**
+ * 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.anim.Interpolators.LINEAR;
+import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
+
+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.View;
+import android.view.animation.Interpolator;
+import android.widget.LinearLayout;
+
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.ItemInfoWithIcon;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.R;
+import com.android.launcher3.WorkspaceItemInfo;
+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.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;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+@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.mIconCurrentTextAlpha;
+                }
+            };
+
+    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 final 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 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).numColumns;
+        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) {
+        removeAllViews();
+        applyPredictionApps();
+    }
+
+    private void applyPredictionApps() {
+        if (getChildCount() != mNumPredictedAppsPerRow) {
+            while (getChildCount() > mNumPredictedAppsPerRow) {
+                removeViewAt(0);
+            }
+            while (getChildCount() < mNumPredictedAppsPerRow) {
+                BubbleTextView icon = (BubbleTextView) mLauncher.getLayoutInflater().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();
+            updateVisibility();
+        }
+        mParent.onHeightUpdated();
+    }
+
+    private List<ItemInfoWithIcon> processPredictedAppComponents(List<ComponentKeyMapper> components) {
+        if (getAppsStore().getApps().isEmpty()) {
+            // 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) {
+                predictedApps.add(info);
+            } else {
+                if (FeatureFlags.IS_DOGFOOD_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(View v, ItemInfo info, LauncherLogProto.Target target,
+            LauncherLogProto.Target targetParent) {
+        for (int i = 0; i < mPredictedApps.size(); i++) {
+            ItemInfoWithIcon appInfo = mPredictedApps.get(i);
+            if (appInfo == info) {
+                targetParent.containerType = LauncherLogProto.ContainerType.PREDICTION;
+                target.predictedRank = i;
+                break;
+            }
+        }
+    }
+
+    public void setTextAlpha(int alpha) {
+        mIconCurrentTextAlpha = alpha;
+        int iconColor = setColorAlphaBound(mIconTextColor, mIconCurrentTextAlpha);
+        for (int i = 0; i < getChildCount(); i++) {
+            ((BubbleTextView) getChildAt(i)).setTextColor(iconColor);
+        }
+    }
+
+    @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 hasContent,
+            PropertySetter setter, Interpolator fadeInterpolator) {
+        boolean isDrawn = getAlpha() > 0;
+        int textAlpha = hasHeaderExtra
+                ? (hasContent ? mIconFullTextAlpha : 0) // Text follows the content visibility
+                : mIconCurrentTextAlpha; // Leave as before
+        if (!isDrawn) {
+            // If the header is not drawn, no need to animate the text alpha
+            setTextAlpha(textAlpha);
+        } else {
+            setter.setInt(this, TEXT_ALPHA, textAlpha, fadeInterpolator);
+        }
+
+        setter.setFloat(mOverviewScrollFactor, AnimatedFloat.VALUE,
+                (hasHeaderExtra && !hasContent) ? 1 : 0, LINEAR);
+        setter.setFloat(mContentAlphaFactor, AnimatedFloat.VALUE, hasHeaderExtra ? 1 : 0,
+                fadeInterpolator);
+    }
+
+    @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
new file mode 100644
index 0000000..085bbc4
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
@@ -0,0 +1,326 @@
+/**
+ * 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.LauncherState.BACKGROUND_APP;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.quickstep.InstantAppResolverImpl.COMPONENT_CLASS_MARKER;
+
+import android.app.prediction.AppPredictor;
+import android.app.prediction.AppTarget;
+import android.content.ComponentName;
+import android.content.Context;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile.OnIDPChangeListener;
+import com.android.launcher3.ItemInfoWithIcon;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.allapps.AllAppsStore.OnUpdateListener;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
+import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.MainThreadInitializedObject;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 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 OnGlobalLayoutListener, 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 onGlobalLayout() {
+        if (mAppsView == null) {
+            return;
+        }
+        if (mPendingState != null && canApplyPredictions(mPendingState)) {
+            applyState(mPendingState);
+            mPendingState = null;
+        }
+        if (mPendingState == null) {
+            mAppsView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+        }
+    }
+
+    private void scheduleApplyPredictedApps(PredictionState state) {
+        boolean registerListener = mPendingState == null;
+        mPendingState = state;
+        if (registerListener) {
+            // OnGlobalLayoutListener is called whenever a view in the view tree changes
+            // visibility. Add a listener and wait until appsView is invisible again.
+            mAppsView.getViewTreeObserver().addOnGlobalLayoutListener(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 ? mCurrentState : mPendingState);
+        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(mContext, key, mDynamicItemCache));
+            }
+        }
+        updateDependencies(state);
+        return state;
+    }
+
+    private void updateDependencies(PredictionState state) {
+        if (!state.isEnabled || mAppsView == null) {
+            return;
+        }
+
+        IconCache iconCache = LauncherAppState.getInstance(mContext).getIconCache();
+        List<String> instantAppsToLoad = new ArrayList<>();
+        List<ShortcutKey> shortcutsToLoad = new ArrayList<>();
+        int total = state.apps.size();
+        for (int i = 0, count = 0; i < total && count < mMaxIconsPerRow; i++) {
+            ComponentKeyMapper mapper = state.apps.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(mAppsView.getAppsStore());
+                if (info != null) {
+                    if (info.usingLowResIcon()) {
+                        // TODO: Update icon cache to support null callbacks.
+                        iconCache.updateIconInBackground(this, info);
+                    }
+                    count++;
+                }
+            }
+        }
+        mDynamicItemCache.cacheItems(shortcutsToLoad, instantAppsToLoad);
+    }
+
+    @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;
+    }
+
+    public static class PredictionState {
+
+        public boolean isEnabled;
+        public List<ComponentKeyMapper> apps;
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
new file mode 100644
index 0000000..a3c2e3c
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
@@ -0,0 +1,223 @@
+/*
+ * 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 com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.view.Gravity;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager.StateHandler;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.graphics.RotationMode;
+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.OverviewToAllAppsTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.QuickSwitchTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.TransposedQuickSwitchTouchController;
+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.TouchInteractionService;
+import com.android.quickstep.views.RecentsView;
+import com.android.systemui.shared.system.WindowManagerWrapper;
+
+import java.util.ArrayList;
+
+/**
+ * Provides recents-related {@link UiFactory} logic and classes.
+ */
+public abstract class RecentsUiFactory {
+
+    public static final boolean GO_LOW_RAM_RECENTS_ENABLED = false;
+    private static final AsyncCommand SET_SHELF_HEIGHT_CMD = (visible, height) ->
+            WindowManagerWrapper.getInstance().setShelfHeight(visible != 0, height);
+
+    public static RotationMode ROTATION_LANDSCAPE = new RotationMode(-90) {
+        @Override
+        public void mapRect(int left, int top, int right, int bottom, Rect out) {
+            out.left = top;
+            out.top = right;
+            out.right = bottom;
+            out.bottom = left;
+        }
+
+        @Override
+        public void mapInsets(Context context, Rect insets, Rect out) {
+            // If there is a display cutout, the top insets in portrait would also include the
+            // cutout, which we will get as the left inset in landscape. Using the max of left and
+            // top allows us to cover both cases (with or without cutout).
+            if (SysUINavigationMode.getMode(context) == NO_BUTTON) {
+                out.top = Math.max(insets.top, insets.left);
+                out.bottom = Math.max(insets.right, insets.bottom);
+                out.left = out.right = 0;
+            } else {
+                out.top = Math.max(insets.top, insets.left);
+                out.bottom = insets.right;
+                out.left = insets.bottom;
+                out.right = 0;
+            }
+        }
+    };
+
+    public static RotationMode ROTATION_SEASCAPE = new RotationMode(90) {
+        @Override
+        public void mapRect(int left, int top, int right, int bottom, Rect out) {
+            out.left = bottom;
+            out.top = left;
+            out.right = top;
+            out.bottom = right;
+        }
+
+        @Override
+        public void mapInsets(Context context, Rect insets, Rect out) {
+            if (SysUINavigationMode.getMode(context) == NO_BUTTON) {
+                out.top = Math.max(insets.top, insets.right);
+                out.bottom = Math.max(insets.left, insets.bottom);
+                out.left = out.right = 0;
+            } else {
+                out.top = Math.max(insets.top, insets.right);
+                out.bottom = insets.left;
+                out.right = insets.bottom;
+                out.left = 0;
+            }
+        }
+
+        @Override
+        public int toNaturalGravity(int absoluteGravity) {
+            int horizontalGravity = absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
+            int verticalGravity = absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK;
+
+            if (horizontalGravity == Gravity.RIGHT) {
+                horizontalGravity = Gravity.LEFT;
+            } else if (horizontalGravity == Gravity.LEFT) {
+                horizontalGravity = Gravity.RIGHT;
+            }
+
+            if (verticalGravity == Gravity.TOP) {
+                verticalGravity = Gravity.BOTTOM;
+            } else if (verticalGravity == Gravity.BOTTOM) {
+                verticalGravity = Gravity.TOP;
+            }
+
+            return ((absoluteGravity & ~Gravity.HORIZONTAL_GRAVITY_MASK)
+                    & ~Gravity.VERTICAL_GRAVITY_MASK)
+                    | horizontalGravity | verticalGravity;
+        }
+    };
+
+    public static RotationMode getRotationMode(DeviceProfile dp) {
+        return !dp.isVerticalBarLayout() ? RotationMode.NORMAL
+                : (dp.isSeascape() ? ROTATION_SEASCAPE : ROTATION_LANDSCAPE);
+    }
+
+    public static TouchController[] createTouchControllers(Launcher launcher) {
+        Mode mode = SysUINavigationMode.getMode(launcher);
+
+        ArrayList<TouchController> list = new ArrayList<>();
+        list.add(launcher.getDragController());
+        if (mode == NO_BUTTON) {
+            list.add(new QuickSwitchTouchController(launcher));
+            list.add(new NavBarToHomeTouchController(launcher));
+            list.add(new FlingAndHoldTouchController(launcher));
+        } else {
+            if (launcher.getDeviceProfile().isVerticalBarLayout()) {
+                list.add(new OverviewToAllAppsTouchController(launcher));
+                list.add(new LandscapeEdgeSwipeController(launcher));
+                if (mode.hasGestures) {
+                    list.add(new TransposedQuickSwitchTouchController(launcher));
+                }
+            } else {
+                list.add(new PortraitStatesTouchController(launcher,
+                        mode.hasGestures /* allowDragToOverview */));
+                if (mode.hasGestures) {
+                    list.add(new QuickSwitchTouchController(launcher));
+                }
+            }
+        }
+
+        list.add(new LauncherTaskViewController(launcher));
+        return list.toArray(new TouchController[list.size()]);
+    }
+
+    /**
+     * Creates and returns the controller responsible for recents view state transitions.
+     *
+     * @param launcher the launcher activity
+     * @return state handler for recents
+     */
+    public static StateHandler createRecentsViewStateController(Launcher launcher) {
+        return new RecentsViewStateController(launcher);
+    }
+
+    /**
+     * Clears the swipe shared state for the current swipe gesture.
+     */
+    public static void clearSwipeSharedState(boolean finishAnimation) {
+        TouchInteractionService.getSwipeSharedState().clearAllState(finishAnimation);
+    }
+
+    /**
+     * Recents logic that triggers when launcher state changes or launcher activity stops/resumes.
+     *
+     * @param launcher the launcher activity
+     */
+    public static void onLauncherStateOrResumeChanged(Launcher launcher) {
+        LauncherState state = launcher.getStateManager().getState();
+        DeviceProfile profile = launcher.getDeviceProfile();
+        boolean visible = (state == NORMAL || state == OVERVIEW) && launcher.isUserActive()
+                && !profile.isVerticalBarLayout();
+        UiThreadHelper.runAsyncCommand(launcher, SET_SHELF_HEIGHT_CMD,
+                visible ? 1 : 0, profile.hotseatBarSizePx);
+
+        if (state == NORMAL) {
+            launcher.<RecentsView>getOverviewPanel().setSwipeDownShouldLaunchApp(false);
+        }
+    }
+
+    private static final class LauncherTaskViewController extends
+            TaskViewTouchController<Launcher> {
+
+        LauncherTaskViewController(Launcher activity) {
+            super(activity);
+        }
+
+        @Override
+        protected boolean isRecentsInteractive() {
+            return mActivity.isInState(OVERVIEW);
+        }
+
+        @Override
+        protected void onUserControlledAnimationCreated(AnimatorPlaybackController animController) {
+            mActivity.getStateManager().setCurrentUserControlledAnimation(animController);
+        }
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
new file mode 100644
index 0000000..b5d8424
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -0,0 +1,98 @@
+/*
+ * 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;
+
+import static com.android.launcher3.LauncherState.RECENTS_CLEAR_ALL_BUTTON;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
+import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
+
+import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.util.FloatProperty;
+
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager.AnimationConfig;
+import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.anim.PropertySetter;
+import com.android.quickstep.views.ClearAllButton;
+import com.android.quickstep.views.LauncherRecentsView;
+import com.android.quickstep.views.RecentsView;
+
+/**
+ * State handler for handling UI changes for {@link LauncherRecentsView}. In addition to managing
+ * the basic view properties, this class also manages changes in the task visuals.
+ */
+@TargetApi(Build.VERSION_CODES.O)
+public final class RecentsViewStateController extends
+        BaseRecentsViewStateController<LauncherRecentsView> {
+
+    public RecentsViewStateController(Launcher launcher) {
+        super(launcher);
+    }
+
+    @Override
+    public void setState(@NonNull LauncherState state) {
+        super.setState(state);
+        if (state.overviewUi) {
+            mRecentsView.updateEmptyMessage();
+            mRecentsView.resetTaskVisuals();
+        }
+        setAlphas(PropertySetter.NO_ANIM_PROPERTY_SETTER, state.getVisibleElements(mLauncher));
+        mRecentsView.setFullscreenProgress(state.getOverviewFullscreenProgress());
+    }
+
+    @Override
+    void setStateWithAnimationInternal(@NonNull final LauncherState toState,
+            @NonNull AnimatorSetBuilder builder, @NonNull AnimationConfig config) {
+        super.setStateWithAnimationInternal(toState, builder, config);
+
+        if (!toState.overviewUi) {
+            builder.addOnFinishRunnable(mRecentsView::resetTaskVisuals);
+        }
+
+        if (toState.overviewUi) {
+            ValueAnimator updateAnim = ValueAnimator.ofFloat(0, 1);
+            updateAnim.addUpdateListener(valueAnimator -> {
+                // While animating into recents, update the visible task data as needed
+                mRecentsView.loadVisibleTaskData();
+            });
+            updateAnim.setDuration(config.duration);
+            builder.play(updateAnim);
+            mRecentsView.updateEmptyMessage();
+        }
+
+        PropertySetter propertySetter = config.getPropertySetter(builder);
+        setAlphas(propertySetter, toState.getVisibleElements(mLauncher));
+        float fullscreenProgress = toState.getOverviewFullscreenProgress();
+        propertySetter.setFloat(mRecentsView, FULLSCREEN_PROGRESS, fullscreenProgress, LINEAR);
+    }
+
+    private void setAlphas(PropertySetter propertySetter, int visibleElements) {
+        boolean hasClearAllButton = (visibleElements & RECENTS_CLEAR_ALL_BUTTON) != 0;
+        propertySetter.setFloat(mRecentsView.getClearAllButton(), ClearAllButton.VISIBILITY_ALPHA,
+                hasClearAllButton ? 1f : 0f, LINEAR);
+    }
+
+    @Override
+    FloatProperty<RecentsView> getContentAlphaProperty() {
+        return CONTENT_ALPHA;
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
new file mode 100644
index 0000000..5ee08c1
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -0,0 +1,99 @@
+/*
+ * 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.states;
+
+import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
+
+import com.android.launcher3.AbstractFloatingView;
+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;
+import com.android.quickstep.views.TaskView;
+
+/**
+ * State indicating that the Launcher is behind an app
+ */
+public class BackgroundAppState extends OverviewState {
+
+    private static final int STATE_FLAGS =
+            FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_DISABLE_ACCESSIBILITY
+                    | FLAG_DISABLE_INTERACTION;
+
+    public BackgroundAppState(int id) {
+        this(id, LauncherLogProto.ContainerType.TASKSWITCHER);
+    }
+
+    protected BackgroundAppState(int id, int logContainer) {
+        super(id, logContainer, OVERVIEW_TRANSITION_MS, STATE_FLAGS);
+    }
+
+    @Override
+    public void onStateEnabled(Launcher launcher) {
+        AbstractFloatingView.closeAllOpenViews(launcher, false);
+    }
+
+    @Override
+    public float getVerticalProgress(Launcher launcher) {
+        if (launcher.getDeviceProfile().isVerticalBarLayout()) {
+            return super.getVerticalProgress(launcher);
+        }
+        int transitionLength = LayoutUtils.getShelfTrackingDistance(launcher,
+                launcher.getDeviceProfile());
+        AllAppsTransitionController controller = launcher.getAllAppsController();
+        float scrollRange = Math.max(controller.getShiftRange(), 1);
+        float progressDelta = (transitionLength / scrollRange);
+        return super.getVerticalProgress(launcher) + progressDelta;
+    }
+
+    @Override
+    public ScaleAndTranslation getOverviewScaleAndTranslation(Launcher launcher) {
+        // Initialize the recents view scale to what it would be when starting swipe up
+        RecentsView recentsView = launcher.getOverviewPanel();
+        int taskCount = recentsView.getTaskViewCount();
+        if (taskCount == 0) {
+            return super.getOverviewScaleAndTranslation(launcher);
+        }
+        TaskView dummyTask = recentsView.getTaskViewAt(Math.max(taskCount - 1,
+                recentsView.getCurrentPage()));
+        return recentsView.getTempClipAnimationHelper().updateForFullscreenOverview(dummyTask)
+                .getScaleAndTranslation();
+    }
+
+    @Override
+    public float getOverviewFullscreenProgress() {
+        return 1;
+    }
+
+    @Override
+    public int getVisibleElements(Launcher launcher) {
+        return super.getVisibleElements(launcher)
+                & ~RECENTS_CLEAR_ALL_BUTTON & ~VERTICAL_SWIPE_INDICATOR;
+    }
+
+    @Override
+    public ScaleAndTranslation getHotseatScaleAndTranslation(Launcher launcher) {
+        if ((getVisibleElements(launcher) & HOTSEAT_ICONS) != 0) {
+            // Translate hotseat offscreen if we show it in overview.
+            ScaleAndTranslation scaleAndTranslation = super.getHotseatScaleAndTranslation(launcher);
+            scaleAndTranslation.translationY = LayoutUtils.getShelfTrackingDistance(launcher,
+                    launcher.getDeviceProfile());
+            return scaleAndTranslation;
+        }
+        return super.getHotseatScaleAndTranslation(launcher);
+    }
+}
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
new file mode 100644
index 0000000..c954762
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java
@@ -0,0 +1,48 @@
+/*
+ * 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 static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X;
+import static com.android.launcher3.anim.Interpolators.INSTANT;
+import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.R;
+import com.android.launcher3.anim.AnimatorSetBuilder;
+
+public class OverviewPeekState extends OverviewState {
+    public OverviewPeekState(int id) {
+        super(id);
+    }
+
+    @Override
+    public ScaleAndTranslation getOverviewScaleAndTranslation(Launcher launcher) {
+        ScaleAndTranslation result = super.getOverviewScaleAndTranslation(launcher);
+        result.translationX = NORMAL.getOverviewScaleAndTranslation(launcher).translationX
+                - launcher.getResources().getDimension(R.dimen.overview_peek_distance);
+        return result;
+    }
+
+    @Override
+    public void prepareForAtomicAnimation(Launcher launcher, LauncherState fromState,
+            AnimatorSetBuilder builder) {
+        if (this == OVERVIEW_PEEK && fromState == NORMAL) {
+            builder.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT);
+            builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_7);
+        }
+    }
+}
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
new file mode 100644
index 0000000..5543860
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -0,0 +1,215 @@
+/*
+ * 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 android.view.View.VISIBLE;
+
+import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
+import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE;
+import static com.android.launcher3.anim.Interpolators.ACCEL;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
+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.logging.LoggerUtils.newContainerTarget;
+import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE;
+
+import android.graphics.Rect;
+import android.view.View;
+
+import com.android.launcher3.AbstractFloatingView;
+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.allapps.DiscoveryBounce;
+import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.uioverrides.UiFactory;
+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.views.RecentsView;
+import com.android.quickstep.views.TaskView;
+
+/**
+ * Definition for overview state
+ */
+public class OverviewState extends LauncherState {
+
+    // Scale recents takes before animating in
+    private static final float RECENTS_PREPARE_SCALE = 1.33f;
+
+    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_DISABLE_ACCESSIBILITY;
+
+    public OverviewState(int id) {
+        this(id, OVERVIEW_TRANSITION_MS, STATE_FLAGS);
+    }
+
+    protected OverviewState(int id, int transitionDuration, int stateFlags) {
+        this(id, ContainerType.TASKSWITCHER, transitionDuration, stateFlags);
+    }
+
+    protected OverviewState(int id, int logContainer, int transitionDuration, int stateFlags) {
+        super(id, logContainer, transitionDuration, stateFlags);
+    }
+
+    @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) {
+            // If the hotseat icons are visible in overview, keep them in their normal position.
+            return super.getWorkspaceScaleAndTranslation(launcher);
+        }
+        return getWorkspaceScaleAndTranslation(launcher);
+    }
+
+    @Override
+    public ScaleAndTranslation getOverviewScaleAndTranslation(Launcher launcher) {
+        return new ScaleAndTranslation(1f, 0f, 0f);
+    }
+
+    @Override
+    public void onStateEnabled(Launcher launcher) {
+        AbstractFloatingView.closeAllOpenViews(launcher);
+    }
+
+    @Override
+    public void onStateTransitionEnd(Launcher launcher) {
+        launcher.getRotationHelper().setCurrentStateRequest(REQUEST_ROTATE);
+        DiscoveryBounce.showForOverviewIfNeeded(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) {
+        if (launcher.getDeviceProfile().isVerticalBarLayout()) {
+            return VERTICAL_SWIPE_INDICATOR | RECENTS_CLEAR_ALL_BUTTON;
+        } else {
+            return HOTSEAT_SEARCH_BOX | VERTICAL_SWIPE_INDICATOR | RECENTS_CLEAR_ALL_BUTTON |
+                    (launcher.getAppsView().getFloatingHeaderView().hasVisibleContent()
+                            ? ALL_APPS_HEADER_EXTRA : HOTSEAT_ICONS);
+        }
+    }
+
+    @Override
+    public float getWorkspaceScrimAlpha(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_desc_recent_apps);
+    }
+
+    public static float getDefaultSwipeHeight(Launcher launcher) {
+        return getDefaultSwipeHeight(launcher.getDeviceProfile());
+    }
+
+    public static float getDefaultSwipeHeight(DeviceProfile dp) {
+        return dp.allAppsCellHeightPx - dp.allAppsIconTextSizePx;
+    }
+
+    @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);
+        }
+    }
+
+    @Override
+    public void prepareForAtomicAnimation(Launcher launcher, LauncherState fromState,
+            AnimatorSetBuilder builder) {
+        if (fromState == NORMAL && this == OVERVIEW) {
+            if (SysUINavigationMode.getMode(launcher) == SysUINavigationMode.Mode.NO_BUTTON) {
+                builder.setInterpolator(ANIM_WORKSPACE_SCALE, ACCEL);
+                builder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL);
+            } else {
+                builder.setInterpolator(ANIM_WORKSPACE_SCALE, OVERSHOOT_1_2);
+
+                // Scale up the recents, if it is not coming from the side
+                RecentsView overview = launcher.getOverviewPanel();
+                if (overview.getVisibility() != VISIBLE || overview.getContentAlpha() == 0) {
+                    SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE);
+                }
+            }
+            builder.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2);
+            builder.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2);
+            builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_7);
+            builder.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2);
+        }
+    }
+
+    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);
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
new file mode 100644
index 0000000..6c9f46f
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
@@ -0,0 +1,68 @@
+/*
+ * 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 android.os.Handler;
+import android.os.Looper;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskView;
+
+/**
+ * State to indicate we are about to launch a recent task. Note that this state is only used when
+ * quick switching from launcher; quick switching from an app uses WindowTransformSwipeHelper.
+ * @see com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget#NEW_TASK
+ */
+public class QuickSwitchState extends BackgroundAppState {
+
+    private static final String TAG = "QuickSwitchState";
+
+    public QuickSwitchState(int id) {
+        super(id, LauncherLogProto.ContainerType.APP);
+    }
+
+    @Override
+    public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
+        float shiftRange = launcher.getAllAppsController().getShiftRange();
+        float shiftProgress = getVerticalProgress(launcher) - NORMAL.getVerticalProgress(launcher);
+        float translationY = shiftProgress * shiftRange;
+        return new ScaleAndTranslation(1, 0, translationY);
+    }
+
+    @Override
+    public int getVisibleElements(Launcher launcher) {
+        return NONE;
+    }
+
+    @Override
+    public void onStateTransitionEnd(Launcher launcher) {
+        TaskView tasktolaunch = launcher.<RecentsView>getOverviewPanel().getTaskViewAt(0);
+        if (tasktolaunch != null) {
+            tasktolaunch.launchTask(false, success -> {
+                if (!success) {
+                    launcher.getStateManager().goToState(OVERVIEW);
+                    tasktolaunch.notifyTaskLaunchFailed(TAG);
+                } else {
+                    launcher.getStateManager().moveToRestState();
+                }
+            }, new Handler(Looper.getMainLooper()));
+        } else {
+            launcher.getStateManager().goToState(NORMAL);
+        }
+    }
+}
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
new file mode 100644
index 0000000..3fe4bcf
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
@@ -0,0 +1,178 @@
+/*
+ * 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.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.LauncherStateManager.ANIM_ALL;
+import static com.android.launcher3.LauncherStateManager.ATOMIC_OVERVIEW_PEEK_COMPONENT;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_SCALE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_TRANSLATE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
+import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
+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.view.HapticFeedbackConstants;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
+import com.android.quickstep.OverviewInteractionState;
+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;
+
+    private 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 = getShiftRange() * MAX_DISPLACEMENT_PERCENT;
+    }
+
+    @Override
+    protected long getAtomicDuration() {
+        return 300;
+    }
+
+    @Override
+    public void onDragStart(boolean start) {
+        mMotionPauseDetector.clear();
+
+        super.onDragStart(start);
+
+        if (handlingOverviewAnim()) {
+            mMotionPauseDetector.setOnMotionPauseListener(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;
+                mPeekAnim = mLauncher.getStateManager().createAtomicAnimation(fromState, toState,
+                        new AnimatorSetBuilder(), ATOMIC_OVERVIEW_PEEK_COMPONENT, peekDuration);
+                mPeekAnim.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        mPeekAnim = null;
+                    }
+                });
+                mPeekAnim.start();
+                recentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
+                        HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+            });
+        }
+    }
+
+    /**
+     * @return Whether we are handling the overview animation, rather than
+     * having it as part of the existing animation to the target state.
+     */
+    private boolean handlingOverviewAnim() {
+        int stateFlags = OverviewInteractionState.INSTANCE.get(mLauncher).getSystemUiStateFlags();
+        return mStartState == NORMAL && (stateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0;
+    }
+
+    @Override
+    protected AnimatorSetBuilder getAnimatorSetBuilderForStates(LauncherState fromState,
+            LauncherState toState) {
+        if (fromState == NORMAL && toState == ALL_APPS) {
+            AnimatorSetBuilder builder = new AnimatorSetBuilder();
+
+            // 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;
+        }
+        return super.getAnimatorSetBuilderForStates(fromState, toState);
+    }
+
+    @Override
+    public boolean onDrag(float displacement, MotionEvent event) {
+        float upDisplacement = -displacement;
+        mMotionPauseDetector.setDisallowPause(upDisplacement < mMotionPauseMinDisplacement
+                || upDisplacement > mMotionPauseMaxDisplacement);
+        mMotionPauseDetector.addPosition(displacement, event.getEventTime());
+        return super.onDrag(displacement, event);
+    }
+
+    @Override
+    public void onDragEnd(float velocity, boolean fling) {
+        if (mMotionPauseDetector.isPaused() && handlingOverviewAnim()) {
+            if (mPeekAnim != null) {
+                mPeekAnim.cancel();
+            }
+
+            AnimatorSetBuilder builder = new AnimatorSetBuilder();
+            builder.setInterpolator(ANIM_VERTICAL_PROGRESS, OVERSHOOT_1_2);
+            if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
+                builder.setInterpolator(ANIM_HOTSEAT_SCALE, OVERSHOOT_1_2);
+                builder.setInterpolator(ANIM_HOTSEAT_TRANSLATE, OVERSHOOT_1_2);
+            }
+            AnimatorSet overviewAnim = mLauncher.getStateManager().createAtomicAnimation(
+                    NORMAL, OVERVIEW, builder, ANIM_ALL, ATOMIC_DURATION);
+            overviewAnim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    onSwipeInteractionCompleted(OVERVIEW, Touch.SWIPE);
+                }
+            });
+            overviewAnim.start();
+        } else {
+            super.onDragEnd(velocity, fling);
+        }
+        mMotionPauseDetector.clear();
+    }
+
+    @Override
+    protected void updateAnimatorBuilderOnReinit(AnimatorSetBuilder builder) {
+        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.
+            builder.addFlag(AnimatorSetBuilder.FLAG_DONT_ANIMATE_OVERVIEW);
+        }
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
new file mode 100644
index 0000000..d66af1a
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
@@ -0,0 +1,222 @@
+/*
+ * 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 android.view.View.TRANSLATION_X;
+
+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.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
+import static com.android.launcher3.touch.AbstractStateChangeTouchController.SUCCESS_TRANSITION_PROGRESS;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.view.MotionEvent;
+import android.view.animation.Interpolator;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager.AnimationConfig;
+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.AnimatorSetBuilder;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.compat.AccessibilityManagerCompat;
+import com.android.launcher3.touch.SwipeDetector;
+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.views.RecentsView;
+
+/**
+ * Handles swiping up on the nav bar to go home from launcher, e.g. overview or all apps.
+ */
+public class NavBarToHomeTouchController implements TouchController, SwipeDetector.Listener {
+
+    private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL_3;
+
+    private final Launcher mLauncher;
+    private final SwipeDetector mSwipeDetector;
+    private final float mPullbackDistance;
+
+    private boolean mNoIntercept;
+    private LauncherState mStartState;
+    private LauncherState mEndState = NORMAL;
+    private AnimatorPlaybackController mCurrentAnimation;
+
+    public NavBarToHomeTouchController(Launcher launcher) {
+        mLauncher = launcher;
+        mSwipeDetector = new SwipeDetector(mLauncher, this, SwipeDetector.VERTICAL);
+        mPullbackDistance = mLauncher.getResources().getDimension(R.dimen.home_pullback_distance);
+    }
+
+    @Override
+    public final boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+            mStartState = mLauncher.getStateManager().getState();
+            mNoIntercept = !canInterceptTouch(ev);
+            if (mNoIntercept) {
+                return false;
+            }
+            mSwipeDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_POSITIVE, false);
+        }
+
+        if (mNoIntercept) {
+            return false;
+        }
+
+        onControllerTouchEvent(ev);
+        return mSwipeDetector.isDraggingOrSettling();
+    }
+
+    private boolean canInterceptTouch(MotionEvent ev) {
+        boolean cameFromNavBar = (ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) != 0;
+        if (!cameFromNavBar) {
+            return false;
+        }
+        if (mStartState == OVERVIEW || mStartState == ALL_APPS) {
+            return true;
+        }
+        if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public final boolean onControllerTouchEvent(MotionEvent ev) {
+        return mSwipeDetector.onTouchEvent(ev);
+    }
+
+    private float getShiftRange() {
+        return mLauncher.getDeviceProfile().heightPx;
+    }
+
+    @Override
+    public void onDragStart(boolean start) {
+        initCurrentAnimation();
+    }
+
+    private void initCurrentAnimation() {
+        long accuracy = (long) (getShiftRange() * 2);
+        final AnimatorSet anim = new AnimatorSet();
+        if (mStartState == OVERVIEW) {
+            RecentsView recentsView = mLauncher.getOverviewPanel();
+            float pullbackDist = mPullbackDistance;
+            if (!recentsView.isRtl()) {
+                pullbackDist = -pullbackDist;
+            }
+            Animator pullback = ObjectAnimator.ofFloat(recentsView, TRANSLATION_X, pullbackDist);
+            pullback.setInterpolator(PULLBACK_INTERPOLATOR);
+            anim.play(pullback);
+        } else if (mStartState == ALL_APPS) {
+            AnimatorSetBuilder builder = new AnimatorSetBuilder();
+            AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
+            Animator allAppsProgress = ObjectAnimator.ofFloat(allAppsController, ALL_APPS_PROGRESS,
+                    -mPullbackDistance / allAppsController.getShiftRange());
+            allAppsProgress.setInterpolator(PULLBACK_INTERPOLATOR);
+            builder.play(allAppsProgress);
+            // Slightly fade out all apps content to further distinguish from scrolling.
+            builder.setInterpolator(AnimatorSetBuilder.ANIM_ALL_APPS_FADE, Interpolators
+                    .mapToProgress(PULLBACK_INTERPOLATOR, 0, 0.5f));
+            AnimationConfig config = new AnimationConfig();
+            config.duration = accuracy;
+            allAppsController.setAlphas(mEndState.getVisibleElements(mLauncher), config, builder);
+            anim.play(builder.build());
+        }
+        AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mLauncher);
+        if (topView != null) {
+            Animator hintCloseAnim = topView.createHintCloseAnim(mPullbackDistance);
+            if (hintCloseAnim != null) {
+                hintCloseAnim.setInterpolator(PULLBACK_INTERPOLATOR);
+                anim.play(hintCloseAnim);
+            }
+        }
+        anim.setDuration(accuracy);
+        mCurrentAnimation = AnimatorPlaybackController.wrap(anim, accuracy, this::clearState);
+    }
+
+    private void clearState() {
+        mCurrentAnimation = null;
+        mSwipeDetector.finishedScrolling();
+        mSwipeDetector.setDetectableScrollConditions(0, false);
+    }
+
+    @Override
+    public boolean onDrag(float displacement) {
+        // Only allow swipe up.
+        displacement = Math.min(0, displacement);
+        float progress = Utilities.getProgress(displacement, 0, getShiftRange());
+        mCurrentAnimation.setPlayFraction(progress);
+        return true;
+    }
+
+    @Override
+    public void onDragEnd(float velocity, boolean fling) {
+        final int logAction = fling ? Touch.FLING : Touch.SWIPE;
+        float progress = mCurrentAnimation.getProgressFraction();
+        float interpolatedProgress = PULLBACK_INTERPOLATOR.getInterpolation(progress);
+        boolean success = interpolatedProgress >= SUCCESS_TRANSITION_PROGRESS
+                || (velocity < 0 && fling);
+        if (success) {
+            mLauncher.getStateManager().goToState(mEndState, true,
+                    () -> onSwipeInteractionCompleted(mEndState));
+            if (mStartState != mEndState) {
+                logStateChange(mStartState.containerType, logAction);
+            }
+            AbstractFloatingView topOpenView = AbstractFloatingView.getTopOpenView(mLauncher);
+            if (topOpenView != null) {
+                AbstractFloatingView.closeAllOpenViews(mLauncher);
+                logStateChange(topOpenView.getLogContainerType(), logAction);
+            }
+        } else {
+            // Quickly return to the state we came from (we didn't move far).
+            ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
+            anim.setFloatValues(progress, 0);
+            anim.addListener(new AnimationSuccessListener() {
+                @Override
+                public void onAnimationSuccess(Animator animator) {
+                    onSwipeInteractionCompleted(mStartState);
+                }
+            });
+            anim.setDuration(80).start();
+        }
+    }
+
+    private void onSwipeInteractionCompleted(LauncherState targetState) {
+        clearState();
+        mLauncher.getStateManager().goToState(targetState, false /* animated */);
+        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());
+    }
+}
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
new file mode 100644
index 0000000..73f328b
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/OverviewToAllAppsTouchController.java
@@ -0,0 +1,81 @@
+/*
+ * 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() {
+        return LauncherLogProto.ContainerType.WORKSPACE;
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java
new file mode 100644
index 0000000..20a2487
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java
@@ -0,0 +1,85 @@
+/*
+ * 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.uioverrides.touchcontrollers.PortraitStatesTouchController.isTouchOverHotseat;
+
+import android.view.MotionEvent;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.util.PendingAnimation;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskView;
+
+/**
+ * Helper class for {@link PortraitStatesTouchController} that determines swipeable regions and
+ * animations on the overview state that depend on the recents implementation.
+ */
+public final class PortraitOverviewStateTouchHelper {
+
+    RecentsView mRecentsView;
+    Launcher mLauncher;
+
+    public PortraitOverviewStateTouchHelper(Launcher launcher) {
+        mLauncher = launcher;
+        mRecentsView = launcher.getOverviewPanel();
+    }
+
+    /**
+     * Whether or not {@link PortraitStatesTouchController} should intercept the touch when on the
+     * overview state.
+     *
+     * @param ev the motion event
+     * @return true if we should intercept the motion event
+     */
+    boolean canInterceptTouch(MotionEvent ev) {
+        if (mRecentsView.getChildCount() > 0) {
+            // Allow swiping up in the gap between the hotseat and overview.
+            return ev.getY() >= mRecentsView.getChildAt(0).getBottom();
+        } else {
+            // If there are no tasks, we only intercept if we're below the hotseat height.
+            return isTouchOverHotseat(mLauncher, ev);
+        }
+    }
+
+    /**
+     * Whether or not swiping down to leave overview state should return to the currently running
+     * task app.
+     *
+     * @return true if going back should take the user to the currently running task
+     */
+    boolean shouldSwipeDownReturnToApp() {
+        TaskView taskView = mRecentsView.getTaskViewAt(mRecentsView.getNextPage());
+        return taskView != null && mRecentsView.shouldSwipeDownLaunchApp();
+    }
+
+    /**
+     * Create the animation for going from overview to the task app via swiping. Should only be
+     * called when {@link #shouldSwipeDownReturnToApp()} returns true.
+     *
+     * @param duration how long the animation should be
+     * @return the animation
+     */
+    PendingAnimation createSwipeDownToTaskAppAnimation(long duration) {
+        mRecentsView.setCurrentPage(mRecentsView.getPageNearestToCenterOfScreen());
+        TaskView taskView = mRecentsView.getTaskViewAt(mRecentsView.getCurrentPage());
+        if (taskView == null) {
+            throw new IllegalStateException("There is no task view to animate to.");
+        }
+        return mRecentsView.createTaskLauncherAnimation(taskView, duration);
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
new file mode 100644
index 0000000..18b8af4
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
@@ -0,0 +1,165 @@
+/*
+ * 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.NORMAL;
+import static com.android.launcher3.LauncherState.QUICK_SWITCH;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_Y;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE;
+import static com.android.launcher3.anim.Interpolators.ACCEL_2;
+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.util.SystemUiController.UI_STATE_OVERVIEW;
+import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
+
+import android.view.MotionEvent;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.touch.AbstractStateChangeTouchController;
+import com.android.launcher3.touch.SwipeDetector;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
+import com.android.quickstep.OverviewInteractionState;
+import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.SysUINavigationMode.Mode;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.system.QuickStepContract;
+
+/**
+ * Handles quick switching to a recent task from the home screen.
+ */
+public class QuickSwitchTouchController extends AbstractStateChangeTouchController {
+
+    private @Nullable TaskView mTaskToLaunch;
+
+    public QuickSwitchTouchController(Launcher launcher) {
+        this(launcher, SwipeDetector.HORIZONTAL);
+    }
+
+    protected QuickSwitchTouchController(Launcher l, SwipeDetector.Direction dir) {
+        super(l, dir);
+    }
+
+    @Override
+    protected boolean canInterceptTouch(MotionEvent ev) {
+        if (mCurrentAnimation != null) {
+            return true;
+        }
+        if (!mLauncher.isInState(LauncherState.NORMAL)) {
+            return false;
+        }
+        if ((ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) == 0) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
+        int stateFlags = OverviewInteractionState.INSTANCE.get(mLauncher).getSystemUiStateFlags();
+        if ((stateFlags & SYSUI_STATE_OVERVIEW_DISABLED) != 0) {
+            return NORMAL;
+        }
+        return isDragTowardPositive ? QUICK_SWITCH : NORMAL;
+    }
+
+    @Override
+    public void onDragStart(boolean start) {
+        super.onDragStart(start);
+        mStartContainerType = LauncherLogProto.ContainerType.NAVBAR;
+        mTaskToLaunch = mLauncher.<RecentsView>getOverviewPanel().getTaskViewAt(0);
+    }
+
+    @Override
+    protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
+        super.onSwipeInteractionCompleted(targetState, logAction);
+        mTaskToLaunch = null;
+    }
+
+    @Override
+    protected float initCurrentAnimation(int animComponents) {
+        AnimatorSetBuilder animatorSetBuilder = new AnimatorSetBuilder();
+        setupInterpolators(animatorSetBuilder);
+        long accuracy = (long) (getShiftRange() * 2);
+        mCurrentAnimation = mLauncher.getStateManager().createAnimationToNewWorkspace(mToState,
+                animatorSetBuilder, accuracy, this::clearState, LauncherStateManager.ANIM_ALL);
+        mCurrentAnimation.getAnimationPlayer().addUpdateListener(valueAnimator -> {
+            updateFullscreenProgress((Float) valueAnimator.getAnimatedValue());
+        });
+        return 1 / getShiftRange();
+    }
+
+    private void setupInterpolators(AnimatorSetBuilder animatorSetBuilder) {
+        animatorSetBuilder.setInterpolator(ANIM_WORKSPACE_FADE, DEACCEL_2);
+        animatorSetBuilder.setInterpolator(ANIM_ALL_APPS_FADE, DEACCEL_2);
+        if (SysUINavigationMode.getMode(mLauncher) == Mode.NO_BUTTON) {
+            // Overview lives to the left of workspace, so translate down later than over
+            animatorSetBuilder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL_2);
+            animatorSetBuilder.setInterpolator(ANIM_VERTICAL_PROGRESS, ACCEL_2);
+            animatorSetBuilder.setInterpolator(ANIM_OVERVIEW_SCALE, ACCEL_2);
+            animatorSetBuilder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, ACCEL_2);
+            animatorSetBuilder.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT);
+        } else {
+            animatorSetBuilder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, LINEAR);
+            animatorSetBuilder.setInterpolator(ANIM_VERTICAL_PROGRESS, LINEAR);
+        }
+    }
+
+    @Override
+    protected void updateProgress(float progress) {
+        super.updateProgress(progress);
+        updateFullscreenProgress(Utilities.boundToRange(progress, 0, 1));
+    }
+
+    private void updateFullscreenProgress(float progress) {
+        if (mTaskToLaunch != null) {
+            mTaskToLaunch.setFullscreenProgress(progress);
+            int sysuiFlags = progress > UPDATE_SYSUI_FLAGS_THRESHOLD
+                    ? mTaskToLaunch.getThumbnail().getSysUiStatusNavFlags()
+                    : 0;
+            mLauncher.getSystemUiController().updateUiState(UI_STATE_OVERVIEW, sysuiFlags);
+        }
+    }
+
+    @Override
+    protected float getShiftRange() {
+        return mLauncher.getDeviceProfile().widthPx / 2f;
+    }
+
+    @Override
+    protected int getLogContainerTypeForNormalState() {
+        return LauncherLogProto.ContainerType.NAVBAR;
+    }
+
+    @Override
+    protected int getDirectionForLog() {
+        return Utilities.isRtl(mLauncher.getResources()) ? Direction.LEFT : Direction.RIGHT;
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
new file mode 100644
index 0000000..8e32bb3
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -0,0 +1,309 @@
+/*
+ * 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.touchcontrollers;
+
+import static com.android.launcher3.AbstractFloatingView.TYPE_ACCESSIBLE;
+import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
+import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.view.MotionEvent;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.LauncherAnimUtils;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.touch.SwipeDetector;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
+import com.android.launcher3.util.FlingBlockCheck;
+import com.android.launcher3.util.PendingAnimation;
+import com.android.launcher3.util.TouchController;
+import com.android.launcher3.views.BaseDragLayer;
+import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskView;
+
+/**
+ * Touch controller for handling task view card swipes
+ */
+public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
+        extends AnimatorListenerAdapter implements TouchController, SwipeDetector.Listener {
+
+    private static final String TAG = "OverviewSwipeController";
+
+    // 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;
+
+    protected final T mActivity;
+    private final SwipeDetector mDetector;
+    private final RecentsView mRecentsView;
+    private final int[] mTempCords = new int[2];
+
+    private PendingAnimation mPendingAnimation;
+    private AnimatorPlaybackController mCurrentAnimation;
+    private boolean mCurrentAnimationIsGoingUp;
+
+    private boolean mNoIntercept;
+
+    private float mDisplacementShift;
+    private float mProgressMultiplier;
+    private float mEndDisplacement;
+    private FlingBlockCheck mFlingBlockCheck = new FlingBlockCheck();
+
+    private TaskView mTaskBeingDragged;
+
+    public TaskViewTouchController(T activity) {
+        mActivity = activity;
+        mRecentsView = activity.getOverviewPanel();
+        mDetector = new SwipeDetector(activity, this, SwipeDetector.VERTICAL);
+    }
+
+    private boolean canInterceptTouch() {
+        if (mCurrentAnimation != null) {
+            // If we are already animating from a previous state, we can intercept.
+            return true;
+        }
+        if (AbstractFloatingView.getTopOpenViewWithType(mActivity, TYPE_ACCESSIBLE) != null) {
+            return false;
+        }
+        return isRecentsInteractive();
+    }
+
+    protected abstract boolean isRecentsInteractive();
+
+    protected void onUserControlledAnimationCreated(AnimatorPlaybackController animController) {
+    }
+
+    @Override
+    public void onAnimationCancel(Animator animation) {
+        if (mCurrentAnimation != null && animation == mCurrentAnimation.getTarget()) {
+            clearState();
+        }
+    }
+
+    @Override
+    public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+            mNoIntercept = !canInterceptTouch();
+            if (mNoIntercept) {
+                return false;
+            }
+
+            // Now figure out which direction scroll events the controller will start
+            // calling the callbacks.
+            int directionsToDetectScroll = 0;
+            boolean ignoreSlopWhenSettling = false;
+            if (mCurrentAnimation != null) {
+                directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
+                ignoreSlopWhenSettling = true;
+            } else {
+                mTaskBeingDragged = null;
+
+                for (int i = 0; i < mRecentsView.getTaskViewCount(); i++) {
+                    TaskView view = mRecentsView.getTaskViewAt(i);
+                    if (mRecentsView.isTaskViewVisible(view) && mActivity.getDragLayer()
+                            .isEventOverView(view, ev)) {
+                        mTaskBeingDragged = view;
+                        if (!SysUINavigationMode.getMode(mActivity).hasGestures) {
+                            // Don't allow swipe down to open if we don't support swipe up
+                            // to enter overview.
+                            directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE;
+                        } else {
+                            // The task can be dragged up to dismiss it,
+                            // and down to open if it's the current page.
+                            directionsToDetectScroll = i == mRecentsView.getCurrentPage()
+                                    ? SwipeDetector.DIRECTION_BOTH : SwipeDetector.DIRECTION_POSITIVE;
+                        }
+                        break;
+                    }
+                }
+                if (mTaskBeingDragged == null) {
+                    mNoIntercept = true;
+                    return false;
+                }
+            }
+
+            mDetector.setDetectableScrollConditions(
+                    directionsToDetectScroll, ignoreSlopWhenSettling);
+        }
+
+        if (mNoIntercept) {
+            return false;
+        }
+
+        onControllerTouchEvent(ev);
+        return mDetector.isDraggingOrSettling();
+    }
+
+    @Override
+    public boolean onControllerTouchEvent(MotionEvent ev) {
+        return mDetector.onTouchEvent(ev);
+    }
+
+    private void reInitAnimationController(boolean goingUp) {
+        if (mCurrentAnimation != null && mCurrentAnimationIsGoingUp == goingUp) {
+            // No need to init
+            return;
+        }
+        int scrollDirections = mDetector.getScrollDirections();
+        if (goingUp && ((scrollDirections & SwipeDetector.DIRECTION_POSITIVE) == 0)
+                || !goingUp && ((scrollDirections & SwipeDetector.DIRECTION_NEGATIVE) == 0)) {
+            // 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;
+        }
+
+        mCurrentAnimationIsGoingUp = goingUp;
+        BaseDragLayer dl = mActivity.getDragLayer();
+        long maxDuration = (long) (2 * dl.getHeight());
+
+        if (goingUp) {
+            mPendingAnimation = mRecentsView.createTaskDismissAnimation(mTaskBeingDragged,
+                    true /* animateTaskView */, true /* removeTask */, maxDuration);
+
+            mEndDisplacement = -mTaskBeingDragged.getHeight();
+        } else {
+            mPendingAnimation = mRecentsView.createTaskLauncherAnimation(
+                    mTaskBeingDragged, maxDuration);
+            mPendingAnimation.anim.setInterpolator(Interpolators.ZOOM_IN);
+
+            mTempCords[1] = mTaskBeingDragged.getHeight();
+            dl.getDescendantCoordRelativeToSelf(mTaskBeingDragged, mTempCords);
+            mEndDisplacement = dl.getHeight() - mTempCords[1];
+        }
+
+        if (mCurrentAnimation != null) {
+            mCurrentAnimation.setOnCancelRunnable(null);
+        }
+        mCurrentAnimation = AnimatorPlaybackController
+                .wrap(mPendingAnimation.anim, maxDuration, this::clearState);
+        onUserControlledAnimationCreated(mCurrentAnimation);
+        mCurrentAnimation.getTarget().addListener(this);
+        mCurrentAnimation.dispatchOnStart();
+        mProgressMultiplier = 1 / mEndDisplacement;
+    }
+
+    @Override
+    public void onDragStart(boolean start) {
+        if (mCurrentAnimation == null) {
+            reInitAnimationController(mDetector.wasInitialTouchPositive());
+            mDisplacementShift = 0;
+        } else {
+            mDisplacementShift = mCurrentAnimation.getProgressFraction() / mProgressMultiplier;
+            mCurrentAnimation.pause();
+        }
+        mFlingBlockCheck.unblockFling();
+    }
+
+    @Override
+    public boolean onDrag(float displacement) {
+        float totalDisplacement = displacement + mDisplacementShift;
+        boolean isGoingUp =
+                totalDisplacement == 0 ? mCurrentAnimationIsGoingUp : totalDisplacement < 0;
+        if (isGoingUp != mCurrentAnimationIsGoingUp) {
+            reInitAnimationController(isGoingUp);
+            mFlingBlockCheck.blockFling();
+        } 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);
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public void onDragEnd(float velocity, boolean fling) {
+        final boolean goingToEnd;
+        final int logAction;
+        boolean blockedFling = fling && mFlingBlockCheck.isBlocked();
+        if (blockedFling) {
+            fling = false;
+        }
+        float progress = mCurrentAnimation.getProgressFraction();
+        float interpolatedProgress = mCurrentAnimation.getInterpolatedProgress();
+        if (fling) {
+            logAction = Touch.FLING;
+            boolean goingUp = velocity < 0;
+            goingToEnd = goingUp == mCurrentAnimationIsGoingUp;
+        } else {
+            logAction = Touch.SWIPE;
+            goingToEnd = interpolatedProgress > SUCCESS_TRANSITION_PROGRESS;
+        }
+        long animationDuration = SwipeDetector.calculateDuration(
+                velocity, goingToEnd ? (1 - progress) : progress);
+        if (blockedFling && !goingToEnd) {
+            animationDuration *= LauncherAnimUtils.blockedFlingDurationFactor(velocity);
+        }
+
+        float nextFrameProgress = Utilities.boundToRange(
+                progress + velocity * SINGLE_FRAME_MS / Math.abs(mEndDisplacement), 0f, 1f);
+
+        mCurrentAnimation.setEndAction(() -> onCurrentAnimationEnd(goingToEnd, logAction));
+
+        ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
+        anim.setFloatValues(nextFrameProgress, goingToEnd ? 1f : 0f);
+        anim.setDuration(animationDuration);
+        anim.setInterpolator(scrollInterpolatorForVelocity(velocity));
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+            anim.addUpdateListener(valueAnimator -> {
+                if (mRecentsView.getCurrentPage() != 0 || mCurrentAnimationIsGoingUp) {
+                    mRecentsView.redrawLiveTile(true);
+                }
+            });
+        }
+        if (QUICKSTEP_SPRINGS.get()) {
+            mCurrentAnimation.dispatchOnStartWithVelocity(goingToEnd ? 1f : 0f, velocity);
+        }
+        anim.start();
+    }
+
+    private void onCurrentAnimationEnd(boolean wasSuccess, int logAction) {
+        if (mPendingAnimation != null) {
+            mPendingAnimation.finish(wasSuccess, logAction);
+            mPendingAnimation = null;
+        }
+        clearState();
+    }
+
+    private void clearState() {
+        mDetector.finishedScrolling();
+        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/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java
new file mode 100644
index 0000000..f1e4041
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java
@@ -0,0 +1,44 @@
+/*
+ * 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 com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.touch.SwipeDetector;
+
+public class TransposedQuickSwitchTouchController extends QuickSwitchTouchController {
+
+    public TransposedQuickSwitchTouchController(Launcher launcher) {
+        super(launcher, SwipeDetector.VERTICAL);
+    }
+
+    @Override
+    protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
+        return super.getTargetState(fromState,
+                isDragTowardPositive ^ mLauncher.getDeviceProfile().isSeascape());
+    }
+
+    @Override
+    protected float initCurrentAnimation(int animComponents) {
+        float multiplier = super.initCurrentAnimation(animComponents);
+        return mLauncher.getDeviceProfile().isSeascape() ? multiplier : -multiplier;
+    }
+
+    @Override
+    protected float getShiftRange() {
+        return mLauncher.getDeviceProfile().heightPx / 2f;
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
new file mode 100644
index 0000000..5e77e0a
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -0,0 +1,177 @@
+/*
+ * 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.anim.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
+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.AnimatorSet;
+import android.animation.ValueAnimator;
+import android.graphics.Rect;
+import android.util.Log;
+import android.view.View;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.quickstep.util.ClipAnimationHelper;
+import com.android.quickstep.util.RemoteAnimationProvider;
+import com.android.quickstep.util.RemoteAnimationTargetSet;
+import com.android.quickstep.views.RecentsView;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
+import com.android.systemui.shared.system.TransactionCompat;
+
+/**
+ * Provider for the atomic remote window animation from the app to the overview.
+ *
+ * @param <T> activity that contains the overview
+ */
+final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> implements
+        RemoteAnimationProvider {
+
+    private static final long RECENTS_LAUNCH_DURATION = 250;
+    private static final String TAG = "AppToOverviewAnimationProvider";
+
+    private final ActivityControlHelper<T> mHelper;
+    // The id of the currently running task that is transitioning to overview.
+    private final int mTargetTaskId;
+
+    private T mActivity;
+    private RecentsView mRecentsView;
+
+    AppToOverviewAnimationProvider(ActivityControlHelper<T> helper, int targetTaskId) {
+        mHelper = helper;
+        mTargetTaskId = targetTaskId;
+    }
+
+    /**
+     * 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(mTargetTaskId);
+        AbstractFloatingView.closeAllOpenViews(activity, wasVisible);
+        ActivityControlHelper.AnimationFactory factory =
+                mHelper.prepareRecentsUI(activity, wasVisible,
+                false /* animate activity */, (controller) -> {
+                    controller.dispatchOnStart();
+                    ValueAnimator anim = controller.getAnimationPlayer()
+                            .setDuration(RECENTS_LAUNCH_DURATION);
+                    anim.setInterpolator(FAST_OUT_SLOW_IN);
+                    anim.start();
+                });
+        factory.onRemoteAnimationReceived(null);
+        factory.createActivityController(RECENTS_LAUNCH_DURATION);
+        mActivity = activity;
+        mRecentsView = mActivity.getOverviewPanel();
+        return false;
+    }
+
+    /**
+     * Create remote window animation from the currently running app to the overview panel.
+     *
+     * @param targetCompats the target apps
+     * @return animation from app to overview
+     */
+    @Override
+    public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] targetCompats) {
+        if (mRecentsView != null) {
+            mRecentsView.setRunningTaskIconScaledDown(true);
+        }
+        AnimatorSet anim = new AnimatorSet();
+        anim.addListener(new AnimationSuccessListener() {
+            @Override
+            public void onAnimationSuccess(Animator animator) {
+                if (mRecentsView != null) {
+                    mRecentsView.animateUpRunningTaskIconScale();
+                }
+            }
+        });
+        if (mActivity == null) {
+            Log.e(TAG, "Animation created, before activity");
+            anim.play(ValueAnimator.ofInt(0, 1).setDuration(RECENTS_LAUNCH_DURATION));
+            return anim;
+        }
+
+        RemoteAnimationTargetSet targetSet =
+                new RemoteAnimationTargetSet(targetCompats, MODE_CLOSING);
+
+        // Use the top closing app to determine the insets for the animation
+        RemoteAnimationTargetCompat runningTaskTarget = targetSet.findTask(mTargetTaskId);
+        if (runningTaskTarget == null) {
+            Log.e(TAG, "No closing app");
+            anim.play(ValueAnimator.ofInt(0, 1).setDuration(RECENTS_LAUNCH_DURATION));
+            return anim;
+        }
+
+        final ClipAnimationHelper clipHelper = new ClipAnimationHelper(mActivity);
+
+        // At this point, the activity is already started and laid-out. Get the home-bounds
+        // relative to the screen using the rootView of the activity.
+        int loc[] = new int[2];
+        View rootView = mActivity.getRootView();
+        rootView.getLocationOnScreen(loc);
+        Rect homeBounds = new Rect(loc[0], loc[1],
+                loc[0] + rootView.getWidth(), loc[1] + rootView.getHeight());
+        clipHelper.updateSource(homeBounds, runningTaskTarget);
+
+        Rect targetRect = new Rect();
+        mHelper.getSwipeUpDestinationAndLength(mActivity.getDeviceProfile(), mActivity, targetRect);
+        clipHelper.updateTargetRect(targetRect);
+        clipHelper.prepareAnimation(mActivity.getDeviceProfile(), false /* isOpening */);
+
+        ClipAnimationHelper.TransformParams params = new ClipAnimationHelper.TransformParams()
+                .setSyncTransactionApplier(new SyncRtSurfaceTransactionApplierCompat(rootView));
+        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
+        valueAnimator.setDuration(RECENTS_LAUNCH_DURATION);
+        valueAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
+        valueAnimator.addUpdateListener((v) -> {
+            params.setProgress((float) v.getAnimatedValue());
+            clipHelper.applyTransform(targetSet, params);
+        });
+
+        if (targetSet.isAnimatingHome()) {
+            // If we are animating home, fade in the opening targets
+            RemoteAnimationTargetSet openingSet =
+                    new RemoteAnimationTargetSet(targetCompats, MODE_OPENING);
+
+            TransactionCompat transaction = new TransactionCompat();
+            valueAnimator.addUpdateListener((v) -> {
+                for (RemoteAnimationTargetCompat app : openingSet.apps) {
+                    transaction.setAlpha(app.leash, (float) v.getAnimatedValue());
+                }
+                transaction.apply();
+            });
+        }
+        anim.play(valueAnimator);
+        return anim;
+    }
+
+    /**
+     * 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/FallbackActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
new file mode 100644
index 0000000..c43155b
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
@@ -0,0 +1,238 @@
+/*
+ * 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.anim.Interpolators.LINEAR;
+import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
+import static com.android.quickstep.fallback.FallbackRecentsView.ZOOM_PROGRESS;
+import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.graphics.Rect;
+import android.graphics.RectF;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.quickstep.fallback.FallbackRecentsView;
+import com.android.quickstep.util.LayoutUtils;
+import com.android.quickstep.util.RemoteAnimationTargetSet;
+import com.android.quickstep.views.RecentsView;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+import java.util.function.BiPredicate;
+import java.util.function.Consumer;
+
+/**
+ * {@link ActivityControlHelper} for recents when the default launcher is different than the
+ * currently running one and apps should interact with the {@link RecentsActivity} as opposed
+ * to the in-launcher one.
+ */
+public final class FallbackActivityControllerHelper implements
+        ActivityControlHelper<RecentsActivity> {
+
+    public FallbackActivityControllerHelper() { }
+
+    @Override
+    public void onTransitionCancelled(RecentsActivity activity, boolean activityVisible) {
+        // TODO:
+    }
+
+    @Override
+    public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect) {
+        LayoutUtils.calculateFallbackTaskSize(context, dp, outRect);
+        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;
+        } else {
+            return dp.heightPx - outRect.bottom;
+        }
+    }
+
+    @Override
+    public void onSwipeUpToRecentsComplete(RecentsActivity activity) {
+        RecentsView recentsView = activity.getOverviewPanel();
+        recentsView.getClearAllButton().setVisibilityAlpha(1);
+        recentsView.setDisallowScrollToClearAll(false);
+    }
+
+    @Override
+    public void onAssistantVisibilityChanged(float visibility) {
+        // TODO:
+    }
+
+    @NonNull
+    @Override
+    public HomeAnimationFactory prepareHomeUI(RecentsActivity activity) {
+        RecentsView recentsView = activity.getOverviewPanel();
+
+        return new HomeAnimationFactory() {
+            @NonNull
+            @Override
+            public RectF getWindowTargetRect() {
+                float centerX = recentsView.getPivotX();
+                float centerY = recentsView.getPivotY();
+                return new RectF(centerX, centerY, centerX, centerY);
+            }
+
+            @NonNull
+            @Override
+            public AnimatorPlaybackController createActivityAnimationToHome() {
+                Animator anim = ObjectAnimator.ofFloat(recentsView, CONTENT_ALPHA, 0);
+                anim.addListener(new AnimationSuccessListener() {
+                    @Override
+                    public void onAnimationSuccess(Animator animator) {
+                        recentsView.startHome();
+                    }
+                });
+                AnimatorSet animatorSet = new AnimatorSet();
+                animatorSet.play(anim);
+                long accuracy = 2 * Math.max(recentsView.getWidth(), recentsView.getHeight());
+                return AnimatorPlaybackController.wrap(animatorSet, accuracy);
+            }
+        };
+    }
+
+    @Override
+    public AnimationFactory prepareRecentsUI(RecentsActivity activity, boolean activityVisible,
+            boolean animateActivity, Consumer<AnimatorPlaybackController> callback) {
+        if (activityVisible) {
+            return (transitionLength) -> { };
+        }
+
+        FallbackRecentsView rv = activity.getOverviewPanel();
+        rv.setContentAlpha(0);
+        rv.getClearAllButton().setVisibilityAlpha(0);
+        rv.setDisallowScrollToClearAll(true);
+
+        boolean fromState = !animateActivity;
+        rv.setInOverviewState(fromState);
+
+        return new AnimationFactory() {
+
+            boolean isAnimatingToRecents = false;
+
+            @Override
+            public void onRemoteAnimationReceived(RemoteAnimationTargetSet targets) {
+                isAnimatingToRecents = targets != null && targets.isAnimatingHome();
+                if (!isAnimatingToRecents) {
+                    rv.setContentAlpha(1);
+                }
+                createActivityController(getSwipeUpDestinationAndLength(
+                        activity.getDeviceProfile(), activity, new Rect()));
+            }
+
+            @Override
+            public void createActivityController(long transitionLength) {
+                AnimatorSet animatorSet = new AnimatorSet();
+                if (isAnimatingToRecents) {
+                    ObjectAnimator anim = ObjectAnimator.ofFloat(rv, CONTENT_ALPHA, 0, 1);
+                    anim.setDuration(transitionLength).setInterpolator(LINEAR);
+                    animatorSet.play(anim);
+                }
+
+                ObjectAnimator anim = ObjectAnimator.ofFloat(rv, ZOOM_PROGRESS, 1, 0);
+                anim.setDuration(transitionLength).setInterpolator(LINEAR);
+                animatorSet.play(anim);
+
+                AnimatorPlaybackController controller =
+                        AnimatorPlaybackController.wrap(animatorSet, transitionLength);
+
+                // Since we are changing the start position of the UI, reapply the state, at the end
+                controller.setEndAction(() -> {
+                    boolean endState = true;
+                    rv.setInOverviewState(controller.getInterpolatedProgress() > 0.5 ?
+                                    endState : fromState);
+                });
+
+                callback.accept(controller);
+            }
+        };
+    }
+
+    @Override
+    public ActivityInitListener createActivityInitListener(
+            BiPredicate<RecentsActivity, Boolean> onInitListener) {
+        return new RecentsActivityTracker(onInitListener);
+    }
+
+    @Nullable
+    @Override
+    public RecentsActivity getCreatedActivity() {
+        return RecentsActivityTracker.getCurrentActivity();
+    }
+
+    @Nullable
+    @Override
+    public RecentsView getVisibleRecentsView() {
+        RecentsActivity activity = getCreatedActivity();
+        if (activity != null && activity.hasWindowFocus()) {
+            return activity.getOverviewPanel();
+        }
+        return null;
+    }
+
+    @Override
+    public boolean switchToRecentsIfVisible(Runnable onCompleteCallback) {
+        return false;
+    }
+
+    @Override
+    public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target) {
+        // TODO: Remove this once b/77875376 is fixed
+        return target.sourceContainerBounds;
+    }
+
+    @Override
+    public boolean shouldMinimizeSplitScreen() {
+        // TODO: Remove this once b/77875376 is fixed
+        return false;
+    }
+
+    @Override
+    public int getContainerType() {
+        RecentsActivity activity = getCreatedActivity();
+        boolean visible = activity != null && activity.isStarted() && activity.hasWindowFocus();
+        return visible
+                ? LauncherLogProto.ContainerType.SIDELOADED_LAUNCHER
+                : LauncherLogProto.ContainerType.APP;
+    }
+
+    @Override
+    public boolean isInLiveTileMode() {
+        return false;
+    }
+
+    @Override
+    public void onLaunchTaskFailed(RecentsActivity activity) {
+        // TODO: probably go back to overview instead.
+        activity.<RecentsView>getOverviewPanel().startHome();
+    }
+
+    @Override
+    public void onLaunchTaskSuccess(RecentsActivity activity) {
+        activity.onTaskLaunched();
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
new file mode 100644
index 0000000..b2a71a4
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
@@ -0,0 +1,496 @@
+/*
+ * 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.View.TRANSLATION_Y;
+
+import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.LauncherAppTransitionManagerImpl.INDEX_RECENTS_FADE_ANIM;
+import static com.android.launcher3.LauncherAppTransitionManagerImpl.INDEX_RECENTS_TRANSLATE_X_ANIM;
+import static com.android.launcher3.LauncherAppTransitionManagerImpl.INDEX_SHELF_ANIM;
+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.LauncherStateManager.ANIM_ALL;
+import static com.android.launcher3.anim.Interpolators.ACCEL_2;
+import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
+import static com.android.launcher3.anim.Interpolators.INSTANT;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.quickstep.WindowTransformSwipeHandler.RECENTS_ATTACH_DURATION;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.content.Context;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Region;
+import android.os.UserHandle;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.animation.Interpolator;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherInitListenerEx;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager;
+import com.android.launcher3.allapps.DiscoveryBounce;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.uioverrides.states.OverviewState;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.views.FloatingIconView;
+import com.android.quickstep.SysUINavigationMode.Mode;
+import com.android.quickstep.util.LayoutUtils;
+import com.android.quickstep.util.StaggeredWorkspaceAnim;
+import com.android.quickstep.views.LauncherRecentsView;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+import java.util.function.BiPredicate;
+import java.util.function.Consumer;
+
+/**
+ * {@link ActivityControlHelper} for the in-launcher recents.
+ */
+public final class LauncherActivityControllerHelper implements ActivityControlHelper<Launcher> {
+
+    private Runnable mAdjustInterpolatorsRunnable;
+
+    @Override
+    public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect) {
+        LayoutUtils.calculateLauncherTaskSize(context, dp, outRect);
+        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;
+        } else {
+            return LayoutUtils.getShelfTrackingDistance(context, dp);
+        }
+    }
+
+    @Override
+    public void onTransitionCancelled(Launcher activity, boolean activityVisible) {
+        LauncherState startState = activity.getStateManager().getRestState();
+        activity.getStateManager().goToState(startState, activityVisible);
+    }
+
+    @Override
+    public void onSwipeUpToRecentsComplete(Launcher activity) {
+        // Re apply state in case we did something funky during the transition.
+        activity.getStateManager().reapplyState();
+        DiscoveryBounce.showForOverviewIfNeeded(activity);
+    }
+
+    @Override
+    public void onSwipeUpToHomeComplete(Launcher activity) {
+        // 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.
+        activity.getStateManager().reapplyState();
+    }
+
+    @Override
+    public void onAssistantVisibilityChanged(float visibility) {
+        Launcher launcher = getCreatedActivity();
+        if (launcher != null) {
+            launcher.onAssistantVisibilityChanged(visibility);
+        }
+    }
+
+    @NonNull
+    @Override
+    public HomeAnimationFactory prepareHomeUI(Launcher activity) {
+        final DeviceProfile dp = activity.getDeviceProfile();
+        final RecentsView recentsView = activity.getOverviewPanel();
+        final TaskView runningTaskView = recentsView.getRunningTaskView();
+        final View workspaceView;
+        if (runningTaskView != null && runningTaskView.getTask().key.getComponent() != null) {
+            workspaceView = activity.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(activity, workspaceView,
+                        true /* hideOriginal */, iconLocation, false /* isOpening */)
+                : null;
+
+        return new HomeAnimationFactory() {
+            @Nullable
+            @Override
+            public View getFloatingView() {
+                return floatingIconView;
+            }
+
+            @NonNull
+            @Override
+            public RectF getWindowTargetRect() {
+                final int halfIconSize = dp.iconSizePx / 2;
+                final float targetCenterX = dp.availableWidthPx / 2f;
+                final float targetCenterY = dp.availableHeightPx - dp.hotseatBarSizePx;
+
+                if (canUseWorkspaceView) {
+                    return iconLocation;
+                } else {
+                    // Fallback to animate to center of screen.
+                    return new RectF(targetCenterX - halfIconSize, targetCenterY - halfIconSize,
+                            targetCenterX + halfIconSize, targetCenterY + halfIconSize);
+                }
+            }
+
+            @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(dp.widthPx, dp.heightPx);
+                return activity.getStateManager().createAnimationToNewWorkspace(NORMAL, accuracy,
+                        0 /* animComponents */);
+            }
+
+            @Override
+            public void playAtomicAnimation(float velocity) {
+                // Setup workspace with 0 duration to prepare for our staggered animation.
+                LauncherStateManager stateManager = activity.getStateManager();
+                AnimatorSetBuilder builder = new AnimatorSetBuilder();
+                // setRecentsAttachedToAppWindow() will animate recents out.
+                builder.addFlag(AnimatorSetBuilder.FLAG_DONT_ANIMATE_OVERVIEW);
+                stateManager.createAtomicAnimation(BACKGROUND_APP, NORMAL, builder, ANIM_ALL, 0);
+                builder.build().start();
+
+                // Stop scrolling so that it doesn't interfere with the translation offscreen.
+                recentsView.getScroller().forceFinished(true);
+
+                new StaggeredWorkspaceAnim(activity, workspaceView, velocity).start();
+            }
+        };
+    }
+
+    @Override
+    public AnimationFactory prepareRecentsUI(Launcher activity, boolean activityVisible,
+            boolean animateActivity, Consumer<AnimatorPlaybackController> callback) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_OVERVIEW_EVENT_TAG, "prepareRecentsUI");
+        }
+        final LauncherState startState = activity.getStateManager().getState();
+
+        LauncherState resetState = startState;
+        if (startState.disableRestore) {
+            resetState = activity.getStateManager().getRestState();
+        }
+        activity.getStateManager().setRestState(resetState);
+
+        final LauncherState fromState = animateActivity ? BACKGROUND_APP : OVERVIEW;
+        activity.getStateManager().goToState(fromState, false);
+        // Since all apps is not visible, we can safely reset the scroll position.
+        // This ensures then the next swipe up to all-apps starts from scroll 0.
+        activity.getAppsView().reset(false /* animate */);
+
+        // Optimization, hide the all apps view to prevent layout while initializing
+        activity.getAppsView().getContentView().setVisibility(View.GONE);
+
+        return new AnimationFactory() {
+            private ShelfAnimState mShelfState;
+            private boolean mIsAttachedToWindow;
+
+            @Override
+            public void createActivityController(long transitionLength) {
+                createActivityControllerInternal(activity, fromState, transitionLength, callback);
+                // Creating the activity controller animation sometimes reapplies the launcher state
+                // (because we set the animation as the current state animation), so we reapply the
+                // attached state here as well to ensure recents is shown/hidden appropriately.
+                if (SysUINavigationMode.getMode(activity) == Mode.NO_BUTTON) {
+                    setRecentsAttachedToAppWindow(mIsAttachedToWindow, false);
+                }
+            }
+
+            @Override
+            public void adjustActivityControllerInterpolators() {
+                if (mAdjustInterpolatorsRunnable != null) {
+                    mAdjustInterpolatorsRunnable.run();
+                }
+            }
+
+            @Override
+            public void onTransitionCancelled() {
+                activity.getStateManager().goToState(startState, false /* animate */);
+            }
+
+            @Override
+            public void setShelfState(ShelfAnimState shelfState, Interpolator interpolator,
+                    long duration) {
+                if (mShelfState == shelfState) {
+                    return;
+                }
+                mShelfState = shelfState;
+                activity.getStateManager().cancelStateElementAnimation(INDEX_SHELF_ANIM);
+                if (mShelfState == ShelfAnimState.CANCEL) {
+                    return;
+                }
+                float shelfHiddenProgress = BACKGROUND_APP.getVerticalProgress(activity);
+                float shelfOverviewProgress = OVERVIEW.getVerticalProgress(activity);
+                // 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(activity);
+                float shelfPeekingProgress = shelfHiddenProgress
+                        - (shelfHiddenProgress - defaultOverviewProgress) * 0.25f;
+                float toProgress = mShelfState == ShelfAnimState.HIDE
+                        ? shelfHiddenProgress
+                        : mShelfState == ShelfAnimState.PEEK
+                                ? shelfPeekingProgress
+                                : shelfOverviewProgress;
+                Animator shelfAnim = activity.getStateManager()
+                        .createStateElementAnimation(INDEX_SHELF_ANIM, toProgress);
+                shelfAnim.setInterpolator(interpolator);
+                shelfAnim.setDuration(duration).start();
+            }
+
+            @Override
+            public void setRecentsAttachedToAppWindow(boolean attached, boolean animate) {
+                if (mIsAttachedToWindow == attached && animate) {
+                    return;
+                }
+                mIsAttachedToWindow = attached;
+                LauncherRecentsView recentsView = activity.getOverviewPanel();
+                Animator fadeAnim = activity.getStateManager()
+                        .createStateElementAnimation(
+                        INDEX_RECENTS_FADE_ANIM, attached ? 1 : 0);
+
+                int runningTaskIndex = recentsView.getRunningTaskIndex();
+                if (runningTaskIndex == 0) {
+                    // If we are on the first task (we haven't quick switched), translate recents in
+                    // from the side. Calculate the start translation based on current scale/scroll.
+                    float currScale = recentsView.getScaleX();
+                    float scrollOffsetX = recentsView.getScrollOffset();
+
+                    float offscreenX = NORMAL.getOverviewScaleAndTranslation(activity).translationX;
+                    // The first task is hidden, so offset by its width.
+                    int firstTaskWidth = recentsView.getTaskViewAt(0).getWidth();
+                    offscreenX -= (firstTaskWidth + recentsView.getPageSpacing()) * currScale;
+                    // Offset since scale pushes tasks outwards.
+                    offscreenX += firstTaskWidth * (currScale - 1) / 2;
+                    offscreenX = Math.max(0, offscreenX);
+                    if (recentsView.isRtl()) {
+                        offscreenX = -offscreenX;
+                    }
+
+                    float fromTranslationX = attached ? offscreenX - scrollOffsetX : 0;
+                    float toTranslationX = attached ? 0 : offscreenX - scrollOffsetX;
+                    activity.getStateManager()
+                            .cancelStateElementAnimation(INDEX_RECENTS_TRANSLATE_X_ANIM);
+
+                    if (!recentsView.isShown() && animate) {
+                        recentsView.setTranslationX(fromTranslationX);
+                    } else {
+                        fromTranslationX = recentsView.getTranslationX();
+                    }
+
+                    if (!animate) {
+                        recentsView.setTranslationX(toTranslationX);
+                    } else {
+                        activity.getStateManager().createStateElementAnimation(
+                                INDEX_RECENTS_TRANSLATE_X_ANIM,
+                                fromTranslationX, toTranslationX).start();
+                    }
+
+                    fadeAnim.setInterpolator(attached ? INSTANT : ACCEL_2);
+                } else {
+                    fadeAnim.setInterpolator(ACCEL_DEACCEL);
+                }
+                fadeAnim.setDuration(animate ? RECENTS_ATTACH_DURATION : 0).start();
+            }
+        };
+    }
+
+    private void createActivityControllerInternal(Launcher activity, LauncherState fromState,
+            long transitionLength, Consumer<AnimatorPlaybackController> callback) {
+        LauncherState endState = OVERVIEW;
+        if (fromState == endState) {
+            return;
+        }
+
+        AnimatorSet anim = new AnimatorSet();
+        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.
+            anim.play(activity.getStateManager().createStateElementAnimation(
+                    INDEX_SHELF_ANIM,
+                    fromState.getVerticalProgress(activity),
+                    endState.getVerticalProgress(activity)));
+        }
+        playScaleDownAnim(anim, activity, fromState, endState);
+
+        anim.setDuration(transitionLength * 2);
+        anim.setInterpolator(LINEAR);
+        AnimatorPlaybackController controller =
+                AnimatorPlaybackController.wrap(anim, transitionLength * 2);
+        activity.getStateManager().setCurrentUserControlledAnimation(controller);
+
+        // Since we are changing the start position of the UI, reapply the state, at the end
+        controller.setEndAction(() -> {
+            activity.getStateManager().goToState(
+                    controller.getInterpolatedProgress() > 0.5 ? endState : fromState, false);
+        });
+        callback.accept(controller);
+    }
+
+    /**
+     * Scale down recents from the center task being full screen to being in overview.
+     */
+    private void playScaleDownAnim(AnimatorSet anim, Launcher launcher, LauncherState fromState,
+            LauncherState endState) {
+        RecentsView recentsView = launcher.getOverviewPanel();
+        TaskView v = recentsView.getTaskViewAt(recentsView.getCurrentPage());
+        if (v == null) {
+            return;
+        }
+
+        LauncherState.ScaleAndTranslation fromScaleAndTranslation
+                = fromState.getOverviewScaleAndTranslation(launcher);
+        LauncherState.ScaleAndTranslation endScaleAndTranslation
+                = endState.getOverviewScaleAndTranslation(launcher);
+        float fromTranslationY = fromScaleAndTranslation.translationY;
+        float endTranslationY = endScaleAndTranslation.translationY;
+        float fromFullscreenProgress = fromState.getOverviewFullscreenProgress();
+        float endFullscreenProgress = endState.getOverviewFullscreenProgress();
+
+        Animator scale = ObjectAnimator.ofFloat(recentsView, SCALE_PROPERTY,
+                fromScaleAndTranslation.scale, endScaleAndTranslation.scale);
+        Animator translateY = ObjectAnimator.ofFloat(recentsView, TRANSLATION_Y,
+                fromTranslationY, endTranslationY);
+        Animator applyFullscreenProgress = ObjectAnimator.ofFloat(recentsView,
+                RecentsView.FULLSCREEN_PROGRESS, fromFullscreenProgress, endFullscreenProgress);
+        anim.playTogether(scale, translateY, applyFullscreenProgress);
+
+        mAdjustInterpolatorsRunnable = () -> {
+            // Adjust the translateY interpolator to account for the running task's top inset.
+            // When progress <= 1, this is handled by each task view as they set their fullscreen
+            // progress. However, once we go to progress > 1, fullscreen progress stays at 0, so
+            // recents as a whole needs to translate further to keep up with the app window.
+            TaskView runningTaskView = recentsView.getRunningTaskView();
+            if (runningTaskView == null) {
+                runningTaskView = recentsView.getTaskViewAt(recentsView.getCurrentPage());
+            }
+            TimeInterpolator oldInterpolator = translateY.getInterpolator();
+            Rect fallbackInsets = launcher.getDeviceProfile().getInsets();
+            float extraTranslationY = runningTaskView.getThumbnail().getInsets(fallbackInsets).top;
+            float normalizedTranslationY = extraTranslationY / (fromTranslationY - endTranslationY);
+            translateY.setInterpolator(t -> {
+                float newT = oldInterpolator.getInterpolation(t);
+                return newT <= 1f ? newT : newT + normalizedTranslationY * (newT - 1);
+            });
+        };
+    }
+
+    @Override
+    public ActivityInitListener createActivityInitListener(
+            BiPredicate<Launcher, Boolean> onInitListener) {
+        return new LauncherInitListenerEx(onInitListener);
+    }
+
+    @Nullable
+    @Override
+    public Launcher getCreatedActivity() {
+        LauncherAppState app = LauncherAppState.getInstanceNoCreate();
+        if (app == null) {
+            return null;
+        }
+        return (Launcher) app.getModel().getCallback();
+    }
+
+    @Nullable
+    @UiThread
+    private Launcher getVisibleLauncher() {
+        Launcher launcher = getCreatedActivity();
+        return (launcher != null) && launcher.isStarted() && launcher.hasWindowFocus() ?
+                launcher : null;
+    }
+
+    @Nullable
+    @Override
+    public RecentsView getVisibleRecentsView() {
+        Launcher launcher = getVisibleLauncher();
+        return launcher != null && launcher.getStateManager().getState().overviewUi
+                ? launcher.getOverviewPanel() : null;
+    }
+
+    @Override
+    public boolean switchToRecentsIfVisible(Runnable onCompleteCallback) {
+        Launcher launcher = getVisibleLauncher();
+        if (launcher == null) {
+            return false;
+        }
+
+        launcher.getUserEventDispatcher().logActionCommand(
+                LauncherLogProto.Action.Command.RECENTS_BUTTON,
+                getContainerType(),
+                LauncherLogProto.ContainerType.TASKSWITCHER);
+        launcher.getStateManager().goToState(OVERVIEW,
+                launcher.getStateManager().shouldAnimateStateChange(), onCompleteCallback);
+        return true;
+    }
+
+    @Override
+    public boolean deferStartingActivity(Region activeNavBarRegion, MotionEvent ev) {
+        return activeNavBarRegion.contains((int) ev.getX(), (int) ev.getY());
+    }
+
+    @Override
+    public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target) {
+        return homeBounds;
+    }
+
+    @Override
+    public boolean shouldMinimizeSplitScreen() {
+        return true;
+    }
+
+    @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 &&
+                launcher.isStarted();
+    }
+
+    @Override
+    public void onLaunchTaskFailed(Launcher launcher) {
+        launcher.getStateManager().goToState(OVERVIEW);
+    }
+
+    @Override
+    public void onLaunchTaskSuccess(Launcher launcher) {
+        launcher.getStateManager().moveToRestState();
+    }
+}
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LockScreenRecentsActivity.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LockScreenRecentsActivity.java
new file mode 100644
index 0000000..65f323c
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LockScreenRecentsActivity.java
@@ -0,0 +1,31 @@
+/*
+ * 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.app.Activity;
+import android.os.Bundle;
+
+/**
+ * Empty activity to start a recents transition
+ */
+public class LockScreenRecentsActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        finish();
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/MultiStateCallback.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/MultiStateCallback.java
new file mode 100644
index 0000000..357c9fc
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/MultiStateCallback.java
@@ -0,0 +1,137 @@
+/*
+ * 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;
+
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.launcher3.config.FeatureFlags;
+
+import java.util.StringJoiner;
+import java.util.function.Consumer;
+
+/**
+ * Utility class to help manage multiple callbacks based on different states.
+ */
+public class MultiStateCallback {
+
+    private static final String TAG = "MultiStateCallback";
+    public static final boolean DEBUG_STATES = false;
+
+    private final SparseArray<Runnable> mCallbacks = new SparseArray<>();
+    private final SparseArray<Consumer<Boolean>> mStateChangeHandlers = new SparseArray<>();
+
+    private final String[] mStateNames;
+
+    public MultiStateCallback(String[] stateNames) {
+        mStateNames = DEBUG_STATES ? stateNames : null;
+    }
+
+    private int mState = 0;
+
+    /**
+     * Adds the provided state flags to the global state and executes any callbacks as a result.
+     */
+    public void setState(int stateFlag) {
+        if (DEBUG_STATES) {
+            Log.d(TAG, "[" + System.identityHashCode(this) + "] Adding "
+                    + convertToFlagNames(stateFlag) + " to " + convertToFlagNames(mState));
+        }
+
+        int oldState = mState;
+        mState = mState | stateFlag;
+
+        int count = mCallbacks.size();
+        for (int i = 0; i < count; i++) {
+            int state = mCallbacks.keyAt(i);
+
+            if ((mState & state) == state) {
+                Runnable callback = mCallbacks.valueAt(i);
+                if (callback != null) {
+                    // Set the callback to null, so that it does not run again.
+                    mCallbacks.setValueAt(i, null);
+                    callback.run();
+                }
+            }
+        }
+        notifyStateChangeHandlers(oldState);
+    }
+
+    /**
+     * Adds the provided state flags to the global state and executes any change handlers
+     * as a result.
+     */
+    public void clearState(int stateFlag) {
+        if (DEBUG_STATES) {
+            Log.d(TAG, "[" + System.identityHashCode(this) + "] Removing "
+                    + convertToFlagNames(stateFlag) + " from " + convertToFlagNames(mState));
+        }
+
+        int oldState = mState;
+        mState = mState & ~stateFlag;
+        notifyStateChangeHandlers(oldState);
+    }
+
+    private void notifyStateChangeHandlers(int oldState) {
+        int count = mStateChangeHandlers.size();
+        for (int i = 0; i < count; i++) {
+            int state = mStateChangeHandlers.keyAt(i);
+            boolean wasOn = (state & oldState) == state;
+            boolean isOn = (state & mState) == state;
+
+            if (wasOn != isOn) {
+                mStateChangeHandlers.valueAt(i).accept(isOn);
+            }
+        }
+    }
+
+    /**
+     * Sets the callbacks to be run when the provided states are enabled.
+     * The callback is only run once.
+     */
+    public void addCallback(int stateMask, Runnable callback) {
+        if (FeatureFlags.IS_DOGFOOD_BUILD && mCallbacks.get(stateMask) != null) {
+            throw new IllegalStateException("Multiple callbacks on same state");
+        }
+        mCallbacks.put(stateMask, callback);
+    }
+
+    /**
+     * Sets the handler to be called when the provided states are enabled or disabled.
+     */
+    public void addChangeHandler(int stateMask, Consumer<Boolean> handler) {
+        mStateChangeHandlers.put(stateMask, handler);
+    }
+
+    public int getState() {
+        return mState;
+    }
+
+    public boolean hasStates(int stateMask) {
+        return (mState & stateMask) == stateMask;
+    }
+
+    private String convertToFlagNames(int flags) {
+        StringJoiner joiner = new StringJoiner(", ", "[", " (" + flags + ")]");
+        for (int i = 0; i < mStateNames.length; i++) {
+            if ((flags & (1 << i)) != 0) {
+                joiner.add(mStateNames[i]);
+            }
+        }
+        return joiner.toString();
+    }
+
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
new file mode 100644
index 0000000..6533c63
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
@@ -0,0 +1,232 @@
+/*
+ * 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.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 com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
+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 ActivityManagerWrapper mAM;
+    private final RecentsModel mRecentsModel;
+    private final MainThreadExecutor mMainThreadExecutor;
+    private final OverviewComponentObserver mOverviewComponentObserver;
+
+    private long mLastToggleTime;
+
+    public OverviewCommandHelper(Context context, OverviewComponentObserver observer) {
+        mContext = context;
+        mAM = ActivityManagerWrapper.getInstance();
+        mMainThreadExecutor = new MainThreadExecutor();
+        mRecentsModel = RecentsModel.INSTANCE.get(mContext);
+        mOverviewComponentObserver = observer;
+    }
+
+    public void onOverviewToggle() {
+        // If currently screen pinning, do not enter overview
+        if (mAM.isScreenPinningActive()) {
+            return;
+        }
+
+        mAM.closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+        mMainThreadExecutor.execute(new RecentsActivityCommand<>());
+    }
+
+    public void onOverviewShown(boolean triggeredFromAltTab) {
+        mMainThreadExecutor.execute(new ShowRecentsCommand(triggeredFromAltTab));
+    }
+
+    public void onOverviewHidden() {
+        mMainThreadExecutor.execute(new HideRecentsCommand());
+    }
+
+    public void onTip(int actionType, int viewType) {
+        mMainThreadExecutor.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 mHelper.getVisibleRecentsView() != null;
+        }
+
+        @Override
+        protected void onTransitionComplete() {
+            if (mTriggeredFromAltTab) {
+                RecentsView rv = (RecentsView) mHelper.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 = (TaskView) rv.getPageAt(0);
+                        taskView.requestFocus();
+                    } else {
+                        rv.requestFocus();
+                    }
+                } else {
+                    taskView.requestFocus();
+                }
+            }
+        }
+    }
+
+    private class HideRecentsCommand extends RecentsActivityCommand {
+
+        @Override
+        protected boolean handleCommand(long elapsedTime) {
+            RecentsView recents = (RecentsView) mHelper.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 BaseDraggingActivity> implements Runnable {
+
+        protected final ActivityControlHelper<T> mHelper;
+        private final long mCreateTime;
+        private final AppToOverviewAnimationProvider<T> mAnimationProvider;
+
+        private final long mToggleClickedTime = SystemClock.uptimeMillis();
+        private boolean mUserEventLogged;
+        private ActivityInitListener mListener;
+
+        public RecentsActivityCommand() {
+            mHelper = mOverviewComponentObserver.getActivityControlHelper();
+            mCreateTime = SystemClock.elapsedRealtime();
+            mAnimationProvider =
+                    new AppToOverviewAnimationProvider<>(mHelper, RecentsModel.getRunningTaskId());
+
+            // Preload the plan
+            mRecentsModel.getTasks(null);
+        }
+
+        @Override
+        public void run() {
+            long elapsedTime = mCreateTime - mLastToggleTime;
+            mLastToggleTime = mCreateTime;
+
+            if (handleCommand(elapsedTime)) {
+                // Command already handled.
+                return;
+            }
+
+            if (mHelper.switchToRecentsIfVisible(this::onTransitionComplete)) {
+                // If successfully switched, then return
+                return;
+            }
+
+            // Otherwise, start overview.
+            mListener = mHelper.createActivityInitListener(this::onActivityReady);
+            mListener.registerAndStartActivity(mOverviewComponentObserver.getOverviewIntent(),
+                    this::createWindowAnimation, mContext, mMainThreadExecutor.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 = mHelper.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(T activity, Boolean wasVisible) {
+            if (!mUserEventLogged) {
+                activity.getUserEventDispatcher().logActionCommand(
+                        LauncherLogProto.Action.Command.RECENTS_BUTTON,
+                        mHelper.getContainerType(),
+                        LauncherLogProto.ContainerType.TASKSWITCHER);
+                mUserEventLogged = true;
+            }
+            return mAnimationProvider.onActivityReady(activity, wasVisible);
+        }
+
+        private AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] targetCompats) {
+            if (LatencyTrackerCompat.isEnabled(mContext)) {
+                LatencyTrackerCompat.logToggleRecents(
+                        (int) (SystemClock.uptimeMillis() - mToggleClickedTime));
+            }
+
+            mListener.unregister();
+
+            AnimatorSet animatorSet = mAnimationProvider.createWindowAnimation(targetCompats);
+            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/RecentsActivity.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
new file mode 100644
index 0000000..fc29a56
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
@@ -0,0 +1,184 @@
+/*
+ * 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.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.quickstep.TaskViewUtils.getRecentsWindowAnimator;
+import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.app.ActivityOptions;
+import android.content.res.Configuration;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.View;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.LauncherAnimationRunner;
+import com.android.launcher3.R;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.views.BaseDragLayer;
+import com.android.quickstep.fallback.FallbackRecentsView;
+import com.android.quickstep.fallback.RecentsRootView;
+import com.android.quickstep.util.ClipAnimationHelper;
+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;
+
+/**
+ * A recents activity that shows the recently launched tasks as swipable task cards.
+ * See {@link com.android.quickstep.views.RecentsView}.
+ */
+public final class RecentsActivity extends BaseRecentsActivity {
+
+    private Handler mUiHandler = new Handler(Looper.getMainLooper());
+    private RecentsRootView mRecentsRootView;
+    private FallbackRecentsView mFallbackRecentsView;
+
+    @Override
+    protected void initViews() {
+        setContentView(R.layout.fallback_recents_activity);
+        mRecentsRootView = findViewById(R.id.drag_layer);
+        mFallbackRecentsView = findViewById(R.id.overview_panel);
+        mRecentsRootView.setup();
+    }
+
+    @Override
+    public void onMultiWindowModeChanged(boolean isInMultiWindowMode, Configuration newConfig) {
+        onHandleConfigChanged();
+        super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig);
+    }
+
+    public void onRootViewSizeChanged() {
+        if (isInMultiWindowMode()) {
+            onHandleConfigChanged();
+        }
+    }
+
+    @Override
+    protected void onHandleConfigChanged() {
+        super.onHandleConfigChanged();
+        mRecentsRootView.setup();
+    }
+
+    @Override
+    protected void reapplyUi() {
+        mRecentsRootView.dispatchInsets();
+    }
+
+    @Override
+    protected DeviceProfile createDeviceProfile() {
+        DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(this).getDeviceProfile(this);
+        return (mRecentsRootView != null) && isInMultiWindowMode()
+                ? dp.getMultiWindowProfile(this, mRecentsRootView.getLastKnownSize())
+                : super.createDeviceProfile();
+    }
+
+    @Override
+    public BaseDragLayer getDragLayer() {
+        return mRecentsRootView;
+    }
+
+    @Override
+    public View getRootView() {
+        return mRecentsRootView;
+    }
+
+    @Override
+    public <T extends View> T getOverviewPanel() {
+        return (T) mFallbackRecentsView;
+    }
+
+    @Override
+    public ActivityOptions getActivityLaunchOptions(final View v) {
+        if (!(v instanceof TaskView)) {
+            return null;
+        }
+
+        final TaskView taskView = (TaskView) v;
+        RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(mUiHandler,
+                true /* startAtFrontOfQueue */) {
+
+            @Override
+            public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
+                    AnimationResult result) {
+                AnimatorSet anim = composeRecentsLaunchAnimator(taskView, targetCompats);
+                anim.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        mFallbackRecentsView.resetViewUI();
+                    }
+                });
+                result.setAnimation(anim);
+            }
+        };
+        return ActivityOptionsCompat.makeRemoteAnimation(new RemoteAnimationAdapterCompat(
+                runner, RECENTS_LAUNCH_DURATION,
+                RECENTS_LAUNCH_DURATION - STATUS_BAR_TRANSITION_DURATION
+                        - STATUS_BAR_TRANSITION_PRE_DELAY));
+    }
+
+    /**
+     * Composes the animations for a launch from the recents list if possible.
+     */
+    private AnimatorSet composeRecentsLaunchAnimator(TaskView taskView,
+            RemoteAnimationTargetCompat[] targets) {
+        AnimatorSet target = new AnimatorSet();
+        boolean activityClosing = taskIsATargetWithMode(targets, getTaskId(), MODE_CLOSING);
+        ClipAnimationHelper helper = new ClipAnimationHelper(this);
+        target.play(getRecentsWindowAnimator(taskView, !activityClosing, targets, helper)
+                .setDuration(RECENTS_LAUNCH_DURATION));
+
+        // Found a visible recents task that matches the opening app, lets launch the app from there
+        if (activityClosing) {
+            Animator adjacentAnimation = mFallbackRecentsView
+                    .createAdjacentPageAnimForTaskLaunch(taskView, helper);
+            adjacentAnimation.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
+            adjacentAnimation.setDuration(RECENTS_LAUNCH_DURATION);
+            adjacentAnimation.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mFallbackRecentsView.resetTaskVisuals();
+                }
+            });
+            target.play(adjacentAnimation);
+        }
+        return target;
+    }
+
+    @Override
+    protected void onStart() {
+        // Set the alpha to 1 before calling super, as it may get set back to 0 due to
+        // onActivityStart callback.
+        mFallbackRecentsView.setContentAlpha(1);
+        super.onStart();
+        mFallbackRecentsView.resetTaskVisuals();
+    }
+
+    public void onTaskLaunched() {
+        mFallbackRecentsView.resetTaskVisuals();
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java
new file mode 100644
index 0000000..ddd28a3
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java
@@ -0,0 +1,205 @@
+/*
+ * 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.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_UP;
+
+import static com.android.launcher3.Utilities.FLAG_NO_GESTURES;
+
+import android.view.InputEvent;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+import com.android.launcher3.util.Preconditions;
+import com.android.quickstep.inputconsumers.InputConsumer;
+import com.android.quickstep.util.SwipeAnimationTargetSet;
+import com.android.systemui.shared.system.InputConsumerController;
+
+import java.util.ArrayList;
+import java.util.function.Supplier;
+
+import androidx.annotation.UiThread;
+
+/**
+ * Wrapper around RecentsAnimationController to help with some synchronization
+ */
+public class RecentsAnimationWrapper {
+
+    // A list of callbacks to run when we receive the recents animation target. There are different
+    // than the state callbacks as these run on the current worker thread.
+    private final ArrayList<Runnable> mCallbacks = new ArrayList<>();
+
+    public SwipeAnimationTargetSet targetSet;
+
+    private boolean mWindowThresholdCrossed = false;
+
+    private final InputConsumerController mInputConsumerController;
+    private final Supplier<InputConsumer> mInputProxySupplier;
+
+    private InputConsumer mInputConsumer;
+    private boolean mTouchInProgress;
+
+    private boolean mFinishPending;
+
+    public RecentsAnimationWrapper(InputConsumerController inputConsumerController,
+            Supplier<InputConsumer> inputProxySupplier) {
+        mInputConsumerController = inputConsumerController;
+        mInputProxySupplier = inputProxySupplier;
+    }
+
+    public boolean hasTargets() {
+        return targetSet != null && targetSet.hasTargets();
+    }
+
+    @UiThread
+    public synchronized void setController(SwipeAnimationTargetSet targetSet) {
+        Preconditions.assertUIThread();
+        this.targetSet = targetSet;
+
+        if (targetSet == null) {
+            return;
+        }
+        targetSet.setWindowThresholdCrossed(mWindowThresholdCrossed);
+
+        if (!mCallbacks.isEmpty()) {
+            for (Runnable action : new ArrayList<>(mCallbacks)) {
+                action.run();
+            }
+            mCallbacks.clear();
+        }
+    }
+
+    public synchronized void runOnInit(Runnable action) {
+        if (targetSet == null) {
+            mCallbacks.add(action);
+        } else {
+            action.run();
+        }
+    }
+
+    /** See {@link #finish(boolean, Runnable, boolean)} */
+    @UiThread
+    public void finish(boolean toRecents, Runnable onFinishComplete) {
+        finish(toRecents, onFinishComplete, false /* sendUserLeaveHint */);
+    }
+
+    /**
+     * @param onFinishComplete A callback that runs on the main thread after the animation
+     *                         controller has finished on the background thread.
+     * @param sendUserLeaveHint Determines whether userLeaveHint flag will be set on the pausing
+     *                          activity. If userLeaveHint is true, the activity will enter into
+     *                          picture-in-picture mode upon being paused.
+     */
+    @UiThread
+    public void finish(boolean toRecents, Runnable onFinishComplete, boolean sendUserLeaveHint) {
+        Preconditions.assertUIThread();
+        if (!toRecents) {
+            finishAndClear(false, onFinishComplete, sendUserLeaveHint);
+        } else {
+            if (mTouchInProgress) {
+                mFinishPending = true;
+                // Execute the callback
+                if (onFinishComplete != null) {
+                    onFinishComplete.run();
+                }
+            } else {
+                finishAndClear(true, onFinishComplete, sendUserLeaveHint);
+            }
+        }
+    }
+
+    private void finishAndClear(boolean toRecents, Runnable onFinishComplete,
+            boolean sendUserLeaveHint) {
+        SwipeAnimationTargetSet controller = targetSet;
+        targetSet = null;
+        if (controller != null) {
+            controller.finishController(toRecents, onFinishComplete, sendUserLeaveHint);
+        }
+    }
+
+    public void enableInputConsumer() {
+        if (targetSet != null) {
+            targetSet.enableInputConsumer();
+        }
+    }
+
+    /**
+     * Indicates that the gesture has crossed the window boundary threshold and system UI can be
+     * update the represent the window behind
+     */
+    public void setWindowThresholdCrossed(boolean windowThresholdCrossed) {
+        if (mWindowThresholdCrossed != windowThresholdCrossed) {
+            mWindowThresholdCrossed = windowThresholdCrossed;
+            if (targetSet != null) {
+                targetSet.setWindowThresholdCrossed(windowThresholdCrossed);
+            }
+        }
+    }
+
+    public void enableInputProxy() {
+        mInputConsumerController.setInputListener(this::onInputConsumerEvent);
+    }
+
+    private boolean onInputConsumerEvent(InputEvent ev) {
+        if (ev instanceof MotionEvent) {
+            onInputConsumerMotionEvent((MotionEvent) ev);
+        } else if (ev instanceof KeyEvent) {
+            if (mInputConsumer == null) {
+                mInputConsumer = mInputProxySupplier.get();
+            }
+            mInputConsumer.onKeyEvent((KeyEvent) ev);
+            return true;
+        }
+        return false;
+    }
+
+    private boolean onInputConsumerMotionEvent(MotionEvent ev) {
+        int action = ev.getAction();
+        if (action == ACTION_DOWN) {
+            mTouchInProgress = true;
+            if (mInputConsumer == null) {
+                mInputConsumer = mInputProxySupplier.get();
+            }
+        } else if (action == ACTION_CANCEL || action == ACTION_UP) {
+            // Finish any pending actions
+            mTouchInProgress = false;
+            if (mFinishPending) {
+                mFinishPending = false;
+                finishAndClear(true /* toRecents */, null, false /* sendUserLeaveHint */);
+            }
+        }
+        if (mInputConsumer != null) {
+            int flags = ev.getEdgeFlags();
+            ev.setEdgeFlags(flags | FLAG_NO_GESTURES);
+            mInputConsumer.onMotionEvent(ev);
+            ev.setEdgeFlags(flags);
+        }
+
+        return true;
+    }
+
+    public void setCancelWithDeferredScreenshot(boolean deferredWithScreenshot) {
+        if (targetSet != null) {
+            targetSet.controller.setCancelWithDeferredScreenshot(deferredWithScreenshot);
+        }
+    }
+
+    public SwipeAnimationTargetSet getController() {
+        return targetSet;
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java
new file mode 100644
index 0000000..c55f656
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java
@@ -0,0 +1,159 @@
+/*
+ * 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.quickstep.TouchInteractionService.MAIN_THREAD_EXECUTOR;
+
+import android.util.Log;
+
+import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.util.Preconditions;
+import com.android.quickstep.util.RecentsAnimationListenerSet;
+import com.android.quickstep.util.SwipeAnimationTargetSet;
+import com.android.quickstep.util.SwipeAnimationTargetSet.SwipeAnimationListener;
+import java.io.PrintWriter;
+
+/**
+ * Utility class used to store state information shared across multiple transitions.
+ */
+public class SwipeSharedState implements SwipeAnimationListener {
+
+    private OverviewComponentObserver mOverviewComponentObserver;
+
+    private RecentsAnimationListenerSet mRecentsAnimationListener;
+    private SwipeAnimationTargetSet mLastAnimationTarget;
+
+    private boolean mLastAnimationCancelled = false;
+    private boolean mLastAnimationRunning = false;
+
+    public boolean canGestureBeContinued;
+    public boolean goingToLauncher;
+    public boolean recentsAnimationFinishInterrupted;
+    public int nextRunningTaskId = -1;
+
+    public void setOverviewComponentObserver(OverviewComponentObserver observer) {
+        mOverviewComponentObserver = observer;
+    }
+
+    @Override
+    public final void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet) {
+        mLastAnimationTarget = targetSet;
+
+        mLastAnimationCancelled = false;
+        mLastAnimationRunning = true;
+    }
+
+    private void clearAnimationTarget() {
+        if (mLastAnimationTarget != null) {
+            mLastAnimationTarget.release();
+            mLastAnimationTarget = null;
+        }
+    }
+
+    @Override
+    public final void onRecentsAnimationCanceled() {
+        clearAnimationTarget();
+
+        mLastAnimationCancelled = true;
+        mLastAnimationRunning = false;
+    }
+
+    private void clearListenerState(boolean finishAnimation) {
+        if (mRecentsAnimationListener != null) {
+            mRecentsAnimationListener.removeListener(this);
+            mRecentsAnimationListener.cancelListener();
+            if (mLastAnimationRunning && mLastAnimationTarget != null) {
+                Utilities.postAsyncCallback(MAIN_THREAD_EXECUTOR.getHandler(),
+                        finishAnimation
+                                ? mLastAnimationTarget::finishAnimation
+                                : mLastAnimationTarget::cancelAnimation);
+                mLastAnimationTarget = null;
+            }
+        }
+        mRecentsAnimationListener = null;
+        clearAnimationTarget();
+        mLastAnimationCancelled = false;
+        mLastAnimationRunning = false;
+    }
+
+    private void onSwipeAnimationFinished(SwipeAnimationTargetSet targetSet) {
+        if (mLastAnimationTarget == targetSet) {
+            mLastAnimationRunning = false;
+        }
+    }
+
+    public RecentsAnimationListenerSet newRecentsAnimationListenerSet() {
+        Preconditions.assertUIThread();
+
+        if (mLastAnimationRunning) {
+            String msg = "New animation started before completing old animation";
+            if (FeatureFlags.IS_DOGFOOD_BUILD) {
+                throw new IllegalArgumentException(msg);
+            } else {
+                Log.e("SwipeSharedState", msg, new Exception());
+            }
+        }
+
+        clearListenerState(false /* finishAnimation */);
+        boolean shouldMinimiseSplitScreen = mOverviewComponentObserver == null ? false
+                : mOverviewComponentObserver.getActivityControlHelper().shouldMinimizeSplitScreen();
+        mRecentsAnimationListener = new RecentsAnimationListenerSet(
+                shouldMinimiseSplitScreen, this::onSwipeAnimationFinished);
+        mRecentsAnimationListener.addListener(this);
+        return mRecentsAnimationListener;
+    }
+
+    public RecentsAnimationListenerSet getActiveListener() {
+        return mRecentsAnimationListener;
+    }
+
+    public void applyActiveRecentsAnimationState(SwipeAnimationListener listener) {
+        if (mLastAnimationTarget != null) {
+            listener.onRecentsAnimationStart(mLastAnimationTarget);
+        } else if (mLastAnimationCancelled) {
+            listener.onRecentsAnimationCanceled();
+        }
+    }
+
+    /**
+     * Called when a recents animation has finished, but was interrupted before the next task was
+     * launched. The given {@param runningTaskId} should be used as the running task for the
+     * continuing input consumer.
+     */
+    public void setRecentsAnimationFinishInterrupted(int runningTaskId) {
+        recentsAnimationFinishInterrupted = true;
+        nextRunningTaskId = runningTaskId;
+        mLastAnimationTarget = mLastAnimationTarget.cloneWithoutTargets();
+    }
+
+    public void clearAllState(boolean finishAnimation) {
+        clearListenerState(finishAnimation);
+        canGestureBeContinued = false;
+        recentsAnimationFinishInterrupted = false;
+        nextRunningTaskId = -1;
+        goingToLauncher = false;
+    }
+
+    public void dump(String prefix, PrintWriter pw) {
+        pw.println(prefix + "goingToLauncher=" + goingToLauncher);
+        pw.println(prefix + "canGestureBeContinued=" + canGestureBeContinued);
+        pw.println(prefix + "recentsAnimationFinishInterrupted=" + recentsAnimationFinishInterrupted);
+        pw.println(prefix + "nextRunningTaskId=" + nextRunningTaskId);
+        pw.println(prefix + "lastAnimationCancelled=" + mLastAnimationCancelled);
+        pw.println(prefix + "lastAnimationRunning=" + mLastAnimationRunning);
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
new file mode 100644
index 0000000..b90f6c2
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
@@ -0,0 +1,82 @@
+/*
+ * 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 android.graphics.Matrix;
+import android.view.View;
+
+import com.android.launcher3.BaseActivity;
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.R;
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.ResourceBasedOverride;
+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.recents.model.ThumbnailData;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Factory class to create and add an overlays on the TaskView
+ */
+public class TaskOverlayFactory implements ResourceBasedOverride {
+
+    /** Note that these will be shown in order from top to bottom, if available for the task. */
+    private static final TaskSystemShortcut[] MENU_OPTIONS = new TaskSystemShortcut[]{
+            new TaskSystemShortcut.AppInfo(),
+            new TaskSystemShortcut.SplitScreen(),
+            new TaskSystemShortcut.Pin(),
+            new TaskSystemShortcut.Install(),
+            new TaskSystemShortcut.Freeform()
+    };
+
+    public static final MainThreadInitializedObject<TaskOverlayFactory> INSTANCE =
+            new MainThreadInitializedObject<>(c -> Overrides.getObject(TaskOverlayFactory.class,
+                    c, R.string.task_overlay_factory_class));
+
+    public List<TaskSystemShortcut> getEnabledShortcuts(TaskView taskView) {
+        final ArrayList<TaskSystemShortcut> shortcuts = new ArrayList<>();
+        final BaseDraggingActivity activity = BaseActivity.fromContext(taskView.getContext());
+        for (TaskSystemShortcut menuOption : MENU_OPTIONS) {
+            View.OnClickListener onClickListener =
+                    menuOption.getOnClickListener(activity, taskView);
+            if (onClickListener != null) {
+                shortcuts.add(menuOption);
+            }
+        }
+        return shortcuts;
+    }
+
+    public TaskOverlay createOverlay(TaskThumbnailView thumbnailView) {
+        return new TaskOverlay();
+    }
+
+    public static class TaskOverlay {
+
+        /**
+         * Called when the current task is interactive for the user
+         */
+        public void initOverlay(Task task, ThumbnailData thumbnail, Matrix matrix) { }
+
+        /**
+         * Called when the overlay is no longer used.
+         */
+        public void reset() { }
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java
new file mode 100644
index 0000000..213c5d3
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java
@@ -0,0 +1,339 @@
+/*
+ * 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.Display.DEFAULT_DISPLAY;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
+
+import android.app.Activity;
+import android.app.ActivityOptions;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+import android.view.View;
+
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.R;
+import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.util.InstantAppResolver;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskThumbnailView;
+import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.recents.ISystemUiProxy;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat;
+import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
+import com.android.systemui.shared.recents.view.RecentsTransition;
+import com.android.systemui.shared.system.ActivityCompat;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.ActivityOptionsCompat;
+import com.android.systemui.shared.system.WindowManagerWrapper;
+
+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 class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut {
+
+    private static final String TAG = "TaskSystemShortcut";
+
+    protected T mSystemShortcut;
+
+    public TaskSystemShortcut(T systemShortcut) {
+        super(systemShortcut);
+        mSystemShortcut = systemShortcut;
+    }
+
+    protected TaskSystemShortcut(int iconResId, int labelResId) {
+        super(iconResId, labelResId);
+    }
+
+    @Override
+    public View.OnClickListener getOnClickListener(
+            BaseDraggingActivity activity, ItemInfo itemInfo) {
+        return null;
+    }
+
+    public View.OnClickListener getOnClickListener(BaseDraggingActivity activity, TaskView view) {
+        Task task = view.getTask();
+
+        WorkspaceItemInfo dummyInfo = new WorkspaceItemInfo();
+        dummyInfo.intent = new Intent();
+        ComponentName component = task.getTopComponent();
+        dummyInfo.intent.setComponent(component);
+        dummyInfo.user = UserHandle.of(task.key.userId);
+        dummyInfo.title = TaskUtils.getTitle(activity, task);
+
+        return getOnClickListenerForTask(activity, task, dummyInfo);
+    }
+
+    protected View.OnClickListener getOnClickListenerForTask(
+            BaseDraggingActivity activity, Task task, ItemInfo dummyInfo) {
+        return mSystemShortcut.getOnClickListener(activity, dummyInfo);
+    }
+
+    public static class AppInfo extends TaskSystemShortcut<SystemShortcut.AppInfo> {
+        public AppInfo() {
+            super(new SystemShortcut.AppInfo());
+        }
+    }
+
+    public static abstract class MultiWindow extends TaskSystemShortcut {
+
+        private Handler mHandler;
+
+        public MultiWindow(int iconRes, int textRes) {
+            super(iconRes, textRes);
+            mHandler = new Handler(Looper.getMainLooper());
+        }
+
+        protected abstract boolean isAvailable(BaseDraggingActivity activity, int displayId);
+        protected abstract ActivityOptions makeLaunchOptions(Activity activity);
+        protected abstract boolean onActivityStarted(BaseDraggingActivity activity);
+
+        @Override
+        public View.OnClickListener getOnClickListener(
+                BaseDraggingActivity activity, TaskView taskView) {
+            final Task task  = taskView.getTask();
+            final int taskId = task.key.id;
+            final int displayId = task.key.displayId;
+            if (!task.isDockable) {
+                return null;
+            }
+            if (!isAvailable(activity, displayId)) {
+                return null;
+            }
+            final RecentsView recentsView = activity.getOverviewPanel();
+
+            final TaskThumbnailView thumbnailView = taskView.getThumbnail();
+            return (v -> {
+                final View.OnLayoutChangeListener onLayoutChangeListener =
+                        new View.OnLayoutChangeListener() {
+                            @Override
+                            public void onLayoutChange(View v, int l, int t, int r, int b,
+                                    int oldL, int oldT, int oldR, int oldB) {
+                                taskView.getRootView().removeOnLayoutChangeListener(this);
+                                recentsView.clearIgnoreResetTask(taskId);
+
+                                // Start animating in the side pages once launcher has been resized
+                                recentsView.dismissTask(taskView, false, false);
+                            }
+                        };
+
+                final DeviceProfile.OnDeviceProfileChangeListener onDeviceProfileChangeListener =
+                        new DeviceProfile.OnDeviceProfileChangeListener() {
+                            @Override
+                            public void onDeviceProfileChanged(DeviceProfile dp) {
+                                activity.removeOnDeviceProfileChangeListener(this);
+                                if (dp.isMultiWindowMode) {
+                                    taskView.getRootView().addOnLayoutChangeListener(
+                                            onLayoutChangeListener);
+                                }
+                            }
+                        };
+
+                dismissTaskMenuView(activity);
+
+                ActivityOptions options = makeLaunchOptions(activity);
+                if (options != null
+                        && ActivityManagerWrapper.getInstance().startActivityFromRecents(taskId,
+                                options)) {
+                    if (!onActivityStarted(activity)) {
+                        return;
+                    }
+                    // Add a device profile change listener to kick off animating the side tasks
+                    // once we enter multiwindow mode and relayout
+                    activity.addOnDeviceProfileChangeListener(onDeviceProfileChangeListener);
+
+                    final Runnable animStartedListener = () -> {
+                        // Hide the task view and wait for the window to be resized
+                        // TODO: Consider animating in launcher and do an in-place start activity
+                        //       afterwards
+                        recentsView.setIgnoreResetTask(taskId);
+                        taskView.setAlpha(0f);
+                    };
+
+                    final int[] position = new int[2];
+                    thumbnailView.getLocationOnScreen(position);
+                    final int width = (int) (thumbnailView.getWidth() * taskView.getScaleX());
+                    final int height = (int) (thumbnailView.getHeight() * taskView.getScaleY());
+                    final Rect taskBounds = new Rect(position[0], position[1],
+                            position[0] + width, position[1] + height);
+
+                    // Take the thumbnail of the task without a scrim and apply it back after
+                    float alpha = thumbnailView.getDimAlpha();
+                    thumbnailView.setDimAlpha(0);
+                    Bitmap thumbnail = RecentsTransition.drawViewIntoHardwareBitmap(
+                            taskBounds.width(), taskBounds.height(), thumbnailView, 1f,
+                            Color.BLACK);
+                    thumbnailView.setDimAlpha(alpha);
+
+                    AppTransitionAnimationSpecsFuture future =
+                            new AppTransitionAnimationSpecsFuture(mHandler) {
+                        @Override
+                        public List<AppTransitionAnimationSpecCompat> composeSpecs() {
+                            return Collections.singletonList(new AppTransitionAnimationSpecCompat(
+                                    taskId, thumbnail, taskBounds));
+                        }
+                    };
+                    WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture(
+                            future, animStartedListener, mHandler, true /* scaleUp */,
+                            v.getDisplay().getDisplayId());
+                }
+            });
+        }
+    }
+
+    public static class SplitScreen extends MultiWindow {
+        public SplitScreen() {
+            super(R.drawable.ic_split_screen, R.string.recent_task_option_split_screen);
+        }
+
+        @Override
+        protected boolean isAvailable(BaseDraggingActivity activity, int displayId) {
+            // Don't show menu-item if already in multi-window and the task is from
+            // the secondary display.
+            // TODO(b/118266305): Temporarily disable splitscreen for secondary display while new
+            // implementation is enabled
+            return !activity.getDeviceProfile().isMultiWindowMode
+                    && (displayId == -1 || displayId == DEFAULT_DISPLAY);
+        }
+
+        @Override
+        protected ActivityOptions makeLaunchOptions(Activity activity) {
+            final ActivityCompat act = new ActivityCompat(activity);
+            final int navBarPosition = WindowManagerWrapper.getInstance().getNavBarPosition(
+                    act.getDisplayId());
+            if (navBarPosition == WindowManagerWrapper.NAV_BAR_POS_INVALID) {
+                return null;
+            }
+            boolean dockTopOrLeft = navBarPosition != WindowManagerWrapper.NAV_BAR_POS_LEFT;
+            return ActivityOptionsCompat.makeSplitScreenOptions(dockTopOrLeft);
+        }
+
+        @Override
+        protected boolean onActivityStarted(BaseDraggingActivity activity) {
+            ISystemUiProxy sysUiProxy = RecentsModel.INSTANCE.get(activity).getSystemUiProxy();
+            try {
+                sysUiProxy.onSplitScreenInvoked();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed to notify SysUI of split screen: ", e);
+                return false;
+            }
+            activity.getUserEventDispatcher().logActionOnControl(TAP,
+                    LauncherLogProto.ControlType.SPLIT_SCREEN_TARGET);
+            return true;
+        }
+    }
+
+    public static class Freeform extends MultiWindow {
+        public Freeform() {
+            super(R.drawable.ic_split_screen, R.string.recent_task_option_freeform);
+        }
+
+        @Override
+        protected boolean isAvailable(BaseDraggingActivity activity, int displayId) {
+            return ActivityManagerWrapper.getInstance().supportsFreeformMultiWindow(activity);
+        }
+
+        @Override
+        protected ActivityOptions makeLaunchOptions(Activity activity) {
+            return ActivityOptionsCompat.makeFreeformOptions();
+        }
+
+        @Override
+        protected boolean onActivityStarted(BaseDraggingActivity activity) {
+            Launcher.getLauncher(activity).getStateManager().goToState(LauncherState.NORMAL);
+            return true;
+        }
+    }
+
+    public static class Pin extends TaskSystemShortcut {
+
+        private static final String TAG = Pin.class.getSimpleName();
+
+        private Handler mHandler;
+
+        public Pin() {
+            super(R.drawable.ic_pin, R.string.recent_task_option_pin);
+            mHandler = new Handler(Looper.getMainLooper());
+        }
+
+        @Override
+        public View.OnClickListener getOnClickListener(
+                BaseDraggingActivity activity, TaskView taskView) {
+            ISystemUiProxy sysUiProxy = RecentsModel.INSTANCE.get(activity).getSystemUiProxy();
+            if (sysUiProxy == null) {
+                return null;
+            }
+            if (!ActivityManagerWrapper.getInstance().isScreenPinningEnabled()) {
+                return null;
+            }
+            if (ActivityManagerWrapper.getInstance().isLockToAppActive()) {
+                // We shouldn't be able to pin while an app is locked.
+                return null;
+            }
+            return view -> {
+                Consumer<Boolean> resultCallback = success -> {
+                    if (success) {
+                        try {
+                            sysUiProxy.startScreenPinning(taskView.getTask().key.id);
+                        } catch (RemoteException e) {
+                            Log.w(TAG, "Failed to start screen pinning: ", e);
+                        }
+                    } else {
+                        taskView.notifyTaskLaunchFailed(TAG);
+                    }
+                };
+                taskView.launchTask(true, resultCallback, mHandler);
+                dismissTaskMenuView(activity);
+            };
+        }
+    }
+
+    public static class Install extends TaskSystemShortcut<SystemShortcut.Install> {
+        public Install() {
+            super(new SystemShortcut.Install());
+        }
+
+        @Override
+        protected View.OnClickListener getOnClickListenerForTask(
+                BaseDraggingActivity activity, Task task, ItemInfo itemInfo) {
+            if (InstantAppResolver.newInstance(activity).isInstantApp(activity,
+                        task.getTopComponent().getPackageName())) {
+                return mSystemShortcut.createOnClickListener(activity, itemInfo);
+            }
+            return null;
+        }
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
new file mode 100644
index 0000000..6897c1e
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
@@ -0,0 +1,176 @@
+/*
+ * 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.anim.Interpolators.LINEAR;
+import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.content.ComponentName;
+import android.graphics.RectF;
+import android.view.View;
+
+import com.android.launcher3.BaseActivity;
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Utilities;
+import com.android.quickstep.util.ClipAnimationHelper;
+import com.android.quickstep.util.MultiValueUpdateListener;
+import com.android.quickstep.util.RemoteAnimationTargetSet;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
+
+/**
+ * Utility class for helpful methods related to {@link TaskView} objects and their tasks.
+ */
+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;
+    }
+
+    /**
+     * @return Animator that controls the window of the opening targets for the recents launch
+     * animation.
+     */
+    public static ValueAnimator getRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
+            RemoteAnimationTargetCompat[] targets, final ClipAnimationHelper inOutHelper) {
+        SyncRtSurfaceTransactionApplierCompat applier =
+                new SyncRtSurfaceTransactionApplierCompat(v);
+        ClipAnimationHelper.TransformParams params = new ClipAnimationHelper.TransformParams()
+                .setSyncTransactionApplier(applier);
+
+        final RemoteAnimationTargetSet targetSet =
+                new RemoteAnimationTargetSet(targets, MODE_OPENING);
+        targetSet.addDependentTransactionApplier(applier);
+
+        final RecentsView recentsView = v.getRecentsView();
+        final ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
+        appAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
+        appAnimator.addUpdateListener(new MultiValueUpdateListener() {
+
+            // Defer fading out the view until after the app window gets faded in
+            final FloatProp mViewAlpha = new FloatProp(1f, 0f, 75, 75, LINEAR);
+            final FloatProp mTaskAlpha = new FloatProp(0f, 1f, 0, 75, LINEAR);
+
+
+            final RectF mThumbnailRect;
+
+            {
+                inOutHelper.setTaskAlphaCallback((t, alpha) -> mTaskAlpha.value);
+
+                inOutHelper.prepareAnimation(
+                        BaseActivity.fromContext(v.getContext()).getDeviceProfile(),
+                        true /* isOpening */);
+                inOutHelper.fromTaskThumbnailView(v.getThumbnail(), (RecentsView) v.getParent(),
+                        targetSet.apps.length == 0 ? null : targetSet.apps[0]);
+
+                mThumbnailRect = new RectF(inOutHelper.getTargetRect());
+                mThumbnailRect.offset(-v.getTranslationX(), -v.getTranslationY());
+                Utilities.scaleRectFAboutCenter(mThumbnailRect, 1 / v.getScaleX());
+            }
+
+            @Override
+            public void onUpdate(float percent) {
+                // TODO: Take into account the current fullscreen progress for animating the insets
+                params.setProgress(1 - percent);
+                RectF taskBounds = inOutHelper.applyTransform(targetSet, params);
+                int taskIndex = recentsView.indexOfChild(v);
+                int centerTaskIndex = recentsView.getCurrentPage();
+                boolean parallaxCenterAndAdjacentTask = taskIndex != centerTaskIndex;
+                if (!skipViewChanges && parallaxCenterAndAdjacentTask) {
+                    float scale = taskBounds.width() / mThumbnailRect.width();
+                    v.setScaleX(scale);
+                    v.setScaleY(scale);
+                    v.setTranslationX(taskBounds.centerX() - mThumbnailRect.centerX());
+                    v.setTranslationY(taskBounds.centerY() - mThumbnailRect.centerY());
+                    v.setAlpha(mViewAlpha.value);
+                }
+            }
+        });
+        appAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                targetSet.release();
+            }
+        });
+        return appAnimator;
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
new file mode 100644
index 0000000..53da0f9
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -0,0 +1,742 @@
+/*
+ * 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;
+
+import static android.view.MotionEvent.ACTION_DOWN;
+
+import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
+import static com.android.launcher3.config.FeatureFlags.APPLY_CONFIG_AT_RUNTIME;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_HINTS_IN_OVERVIEW;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.launcher3.config.FeatureFlags.FAKE_LANDSCAPE_UI;
+import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_MONITOR;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
+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_HOME_DISABLED;
+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_OVERVIEW_DISABLED;
+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.annotation.TargetApi;
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.Point;
+import android.graphics.RectF;
+import android.graphics.Region;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Process;
+import android.os.RemoteException;
+import android.text.TextUtils;
+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.WindowManager;
+
+import androidx.annotation.BinderThread;
+import androidx.annotation.UiThread;
+
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.R;
+import com.android.launcher3.ResourceUtils;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.logging.EventLogArray;
+import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.util.LooperExecutor;
+import com.android.launcher3.util.UiThreadHelper;
+import com.android.quickstep.SysUINavigationMode.Mode;
+import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
+import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
+import com.android.quickstep.inputconsumers.AssistantTouchConsumer;
+import com.android.quickstep.inputconsumers.DeviceLockedInputConsumer;
+import com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer;
+import com.android.quickstep.inputconsumers.InputConsumer;
+import com.android.quickstep.inputconsumers.OtherActivityInputConsumer;
+import com.android.quickstep.inputconsumers.OverviewInputConsumer;
+import com.android.quickstep.inputconsumers.OverviewWithoutFocusInputConsumer;
+import com.android.quickstep.inputconsumers.ResetGestureInputConsumer;
+import com.android.quickstep.inputconsumers.ScreenPinnedInputConsumer;
+import com.android.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.InputEventReceiver;
+import com.android.systemui.shared.system.InputConsumerController;
+import com.android.systemui.shared.system.InputMonitorCompat;
+import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
+import com.android.systemui.shared.system.SystemGestureExclusionListenerCompat;
+
+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();
+    }
+
+    public String nextArgExact() {
+        return pollFirst();
+    }
+}
+
+/**
+ * Service connected by system-UI for handling touch interaction.
+ */
+@TargetApi(Build.VERSION_CODES.Q)
+public class TouchInteractionService extends Service implements
+        NavigationModeChangeListener, DisplayListener {
+
+    public static final MainThreadExecutor MAIN_THREAD_EXECUTOR = new MainThreadExecutor();
+    public static final LooperExecutor BACKGROUND_EXECUTOR =
+            new LooperExecutor(UiThreadHelper.getBackgroundLooper());
+
+    public static final EventLogArray TOUCH_INTERACTION_LOG =
+            new EventLogArray("touch_interaction_log", 40);
+
+    private static final String TAG = "TouchInteractionService";
+
+    private final IBinder mMyBinder = new IOverviewProxy.Stub() {
+
+        public void onActiveNavBarRegionChanges(Region region) {
+            mActiveNavBarRegion = region;
+        }
+
+        public void onInitialize(Bundle bundle) {
+            mISystemUiProxy = ISystemUiProxy.Stub
+                    .asInterface(bundle.getBinder(KEY_EXTRA_SYSUI_PROXY));
+            MAIN_THREAD_EXECUTOR.execute(TouchInteractionService.this::initInputMonitor);
+            MAIN_THREAD_EXECUTOR.execute(TouchInteractionService.this::onSystemUiProxySet);
+        }
+
+        @Override
+        public void onOverviewToggle() {
+            mOverviewCommandHelper.onOverviewToggle();
+        }
+
+        @Override
+        public void onOverviewShown(boolean triggeredFromAltTab) {
+            mOverviewCommandHelper.onOverviewShown(triggeredFromAltTab);
+        }
+
+        @Override
+        public void onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
+            if (triggeredFromAltTab && !triggeredFromHomeKey) {
+                // onOverviewShownFromAltTab hides the overview and ends at the target app
+                mOverviewCommandHelper.onOverviewHidden();
+            }
+        }
+
+        @Override
+        public void onTip(int actionType, int viewType) {
+            mOverviewCommandHelper.onTip(actionType, viewType);
+        }
+
+        @Override
+        public void onAssistantAvailable(boolean available) {
+            mAssistantAvailable = available;
+        }
+
+        @Override
+        public void onAssistantVisibilityChanged(float visibility) {
+            mLastAssistantVisibility = visibility;
+            MAIN_THREAD_EXECUTOR.execute(
+                    TouchInteractionService.this::onAssistantVisibilityChanged);
+        }
+
+        public void onBackAction(boolean completed, int downX, int downY, boolean isButton,
+                boolean gestureSwipeLeft) {
+            if (mOverviewComponentObserver == null) {
+                return;
+            }
+
+            final ActivityControlHelper activityControl =
+                    mOverviewComponentObserver.getActivityControlHelper();
+            UserEventDispatcher.newInstance(getBaseContext()).logActionBack(completed, downX, downY,
+                    isButton, gestureSwipeLeft, activityControl.getContainerType());
+        }
+
+        public void onSystemUiStateChanged(int stateFlags) {
+            mSystemUiStateFlags = stateFlags;
+            MAIN_THREAD_EXECUTOR.execute(TouchInteractionService.this::onSystemUiFlagsChanged);
+        }
+
+        /** 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();
+        }
+
+        public void onBind(ISystemUiProxy iSystemUiProxy) { }
+    };
+
+    private static boolean sConnected = false;
+    private static final SwipeSharedState sSwipeSharedState = new SwipeSharedState();
+
+    public static boolean isConnected() {
+        return sConnected;
+    }
+
+    public static SwipeSharedState getSwipeSharedState() {
+        return sSwipeSharedState;
+    }
+
+    private final InputConsumer mResetGestureInputConsumer =
+            new ResetGestureInputConsumer(sSwipeSharedState);
+
+    private ActivityManagerWrapper mAM;
+    private RecentsModel mRecentsModel;
+    private ISystemUiProxy mISystemUiProxy;
+    private OverviewCommandHelper mOverviewCommandHelper;
+    private OverviewComponentObserver mOverviewComponentObserver;
+    private OverviewInteractionState mOverviewInteractionState;
+    private OverviewCallbacks mOverviewCallbacks;
+    private TaskOverlayFactory mTaskOverlayFactory;
+    private InputConsumerController mInputConsumer;
+    private boolean mAssistantAvailable;
+    private float mLastAssistantVisibility = 0;
+    private @SystemUiStateFlags int mSystemUiStateFlags;
+
+    private boolean mIsUserUnlocked;
+    private BroadcastReceiver mUserUnlockedReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
+                initWhenUserUnlocked();
+            }
+        }
+    };
+
+    private InputConsumer mUncheckedConsumer = InputConsumer.NO_OP;
+    private InputConsumer mConsumer = InputConsumer.NO_OP;
+    private Choreographer mMainChoreographer;
+
+    private Region mActiveNavBarRegion = new Region();
+
+    private InputMonitorCompat mInputMonitorCompat;
+    private InputEventReceiver mInputEventReceiver;
+    private Mode mMode = Mode.THREE_BUTTONS;
+    private int mDefaultDisplayId;
+    private final RectF mSwipeTouchRegion = new RectF();
+    private final RectF mAssistantLeftRegion = new RectF();
+    private final RectF mAssistantRightRegion = new RectF();
+
+    private ComponentName mGestureBlockingActivity;
+
+    private Region mExclusionRegion;
+    private SystemGestureExclusionListenerCompat mExclusionListener;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+
+        // Initialize anything here that is needed in direct boot mode.
+        // Everything else should be initialized in initWhenUserUnlocked() below.
+        mMainChoreographer = Choreographer.getInstance();
+        mAM = ActivityManagerWrapper.getInstance();
+
+        if (UserManagerCompat.getInstance(this).isUserUnlocked(Process.myUserHandle())) {
+            initWhenUserUnlocked();
+        } else {
+            mIsUserUnlocked = false;
+            registerReceiver(mUserUnlockedReceiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED));
+        }
+
+        mDefaultDisplayId = getSystemService(WindowManager.class).getDefaultDisplay()
+                .getDisplayId();
+        String blockingActivity = getString(R.string.gesture_blocking_activity);
+        mGestureBlockingActivity = TextUtils.isEmpty(blockingActivity) ? null :
+                ComponentName.unflattenFromString(blockingActivity);
+
+        mExclusionListener = new SystemGestureExclusionListenerCompat(mDefaultDisplayId) {
+            @Override
+            @BinderThread
+            public void onExclusionChanged(Region region) {
+                // Assignments are atomic, it should be safe on binder thread
+                mExclusionRegion = region;
+            }
+        };
+
+        onNavigationModeChanged(SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this));
+        sConnected = true;
+    }
+
+    private void disposeEventHandlers() {
+        if (mInputEventReceiver != null) {
+            mInputEventReceiver.dispose();
+            mInputEventReceiver = null;
+        }
+        if (mInputMonitorCompat != null) {
+            mInputMonitorCompat.dispose();
+            mInputMonitorCompat = null;
+        }
+    }
+
+    private void initInputMonitor() {
+        if (!mMode.hasGestures || mISystemUiProxy == null) {
+            return;
+        }
+        disposeEventHandlers();
+
+        try {
+            mInputMonitorCompat = InputMonitorCompat.fromBundle(mISystemUiProxy
+                    .monitorGestureInput("swipe-up", mDefaultDisplayId), KEY_EXTRA_INPUT_MONITOR);
+            mInputEventReceiver = mInputMonitorCompat.getInputReceiver(Looper.getMainLooper(),
+                    mMainChoreographer, this::onInputEvent);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Unable to create input monitor", e);
+        }
+        initTouchBounds();
+    }
+
+    private int getNavbarSize(String resName) {
+        return ResourceUtils.getNavbarSize(resName, getResources());
+    }
+
+    private void initTouchBounds() {
+        if (!mMode.hasGestures) {
+            return;
+        }
+
+        Display defaultDisplay = getSystemService(WindowManager.class).getDefaultDisplay();
+        Point realSize = new Point();
+        defaultDisplay.getRealSize(realSize);
+        mSwipeTouchRegion.set(0, 0, realSize.x, realSize.y);
+        if (mMode == Mode.NO_BUTTON) {
+            int touchHeight = getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE);
+            mSwipeTouchRegion.top = mSwipeTouchRegion.bottom - touchHeight;
+
+            final int assistantWidth = getResources()
+                    .getDimensionPixelSize(R.dimen.gestures_assistant_width);
+            final float assistantHeight = Math.max(touchHeight,
+                    QuickStepContract.getWindowCornerRadius(getResources()));
+            mAssistantLeftRegion.bottom = mAssistantRightRegion.bottom = mSwipeTouchRegion.bottom;
+            mAssistantLeftRegion.top = mAssistantRightRegion.top =
+                    mSwipeTouchRegion.bottom - assistantHeight;
+
+            mAssistantLeftRegion.left = 0;
+            mAssistantLeftRegion.right = assistantWidth;
+
+            mAssistantRightRegion.right = mSwipeTouchRegion.right;
+            mAssistantRightRegion.left = mSwipeTouchRegion.right - assistantWidth;
+        } else {
+            mAssistantLeftRegion.setEmpty();
+            mAssistantRightRegion.setEmpty();
+            switch (defaultDisplay.getRotation()) {
+                case Surface.ROTATION_90:
+                    mSwipeTouchRegion.left = mSwipeTouchRegion.right
+                            - getNavbarSize(ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE);
+                    break;
+                case Surface.ROTATION_270:
+                    mSwipeTouchRegion.right = mSwipeTouchRegion.left
+                            + getNavbarSize(ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE);
+                    break;
+                default:
+                    mSwipeTouchRegion.top = mSwipeTouchRegion.bottom
+                            - getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE);
+            }
+        }
+    }
+
+    @Override
+    public void onNavigationModeChanged(Mode newMode) {
+        if (mMode.hasGestures != newMode.hasGestures) {
+            if (newMode.hasGestures) {
+                getSystemService(DisplayManager.class).registerDisplayListener(
+                        this, MAIN_THREAD_EXECUTOR.getHandler());
+            } else {
+                getSystemService(DisplayManager.class).unregisterDisplayListener(this);
+            }
+        }
+        mMode = newMode;
+
+        disposeEventHandlers();
+        initInputMonitor();
+
+        if (mMode == Mode.NO_BUTTON) {
+            mExclusionListener.register();
+        } else {
+            mExclusionListener.unregister();
+        }
+    }
+
+    @Override
+    public void onDisplayAdded(int i) { }
+
+    @Override
+    public void onDisplayRemoved(int i) { }
+
+    @Override
+    public void onDisplayChanged(int displayId) {
+        if (displayId != mDefaultDisplayId) {
+            return;
+        }
+
+        initTouchBounds();
+    }
+
+    private void initWhenUserUnlocked() {
+        mRecentsModel = RecentsModel.INSTANCE.get(this);
+        mOverviewComponentObserver = new OverviewComponentObserver(this);
+
+        mOverviewCommandHelper = new OverviewCommandHelper(this, mOverviewComponentObserver);
+        mOverviewInteractionState = OverviewInteractionState.INSTANCE.get(this);
+        mOverviewCallbacks = OverviewCallbacks.get(this);
+        mTaskOverlayFactory = TaskOverlayFactory.INSTANCE.get(this);
+        mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
+        mIsUserUnlocked = true;
+
+        sSwipeSharedState.setOverviewComponentObserver(mOverviewComponentObserver);
+        mInputConsumer.registerInputConsumer();
+        onSystemUiProxySet();
+        onSystemUiFlagsChanged();
+        onAssistantVisibilityChanged();
+
+        // Temporarily disable model preload
+        // new ModelPreload().start(this);
+
+        Utilities.unregisterReceiverSafely(this, mUserUnlockedReceiver);
+    }
+
+    @UiThread
+    private void onSystemUiProxySet() {
+        if (mIsUserUnlocked) {
+            mRecentsModel.setSystemUiProxy(mISystemUiProxy);
+            mOverviewInteractionState.setSystemUiProxy(mISystemUiProxy);
+        }
+    }
+
+    @UiThread
+    private void onSystemUiFlagsChanged() {
+        if (mIsUserUnlocked) {
+            mOverviewInteractionState.setSystemUiStateFlags(mSystemUiStateFlags);
+            mOverviewComponentObserver.onSystemUiStateChanged(mSystemUiStateFlags);
+        }
+    }
+
+    @UiThread
+    private void onAssistantVisibilityChanged() {
+        if (mIsUserUnlocked) {
+            mOverviewComponentObserver.getActivityControlHelper().onAssistantVisibilityChanged(
+                    mLastAssistantVisibility);
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        if (mIsUserUnlocked) {
+            mInputConsumer.unregisterInputConsumer();
+            mOverviewComponentObserver.onDestroy();
+        }
+        disposeEventHandlers();
+        if (mMode.hasGestures) {
+            getSystemService(DisplayManager.class).unregisterDisplayListener(this);
+        }
+
+        sConnected = false;
+        Utilities.unregisterReceiverSafely(this, mUserUnlockedReceiver);
+        SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this);
+        mExclusionListener.unregister();
+
+        super.onDestroy();
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        Log.d(TAG, "Touch service connected");
+        return mMyBinder;
+    }
+
+    private void onInputEvent(InputEvent ev) {
+        if (!(ev instanceof MotionEvent)) {
+            Log.e(TAG, "Unknown event " + ev);
+            return;
+        }
+        MotionEvent event = (MotionEvent) ev;
+        TOUCH_INTERACTION_LOG.addLog("onMotionEvent", event.getActionMasked());
+        if (event.getAction() == ACTION_DOWN) {
+            if (mSwipeTouchRegion.contains(event.getX(), event.getY())) {
+                boolean useSharedState = mConsumer.useSharedSwipeState();
+                mConsumer.onConsumerAboutToBeSwitched();
+                mConsumer = newConsumer(useSharedState, event);
+                TOUCH_INTERACTION_LOG.addLog("setInputConsumer", mConsumer.getType());
+                mUncheckedConsumer = mConsumer;
+            } else if (mIsUserUnlocked && mMode == Mode.NO_BUTTON
+                    && canTriggerAssistantAction(event)) {
+                // Do not change mConsumer as if there is an ongoing QuickSwitch gesture, we should
+                // not interrupt it. QuickSwitch assumes that interruption can only happen if the
+                // next gesture is also quick switch.
+                mUncheckedConsumer =
+                        new AssistantTouchConsumer(this, mISystemUiProxy,
+                                mOverviewComponentObserver.getActivityControlHelper(),
+                                InputConsumer.NO_OP, mInputMonitorCompat);
+            } else {
+                mUncheckedConsumer = InputConsumer.NO_OP;
+            }
+        }
+        mUncheckedConsumer.onMotionEvent(event);
+    }
+
+    private boolean validSystemUiFlags() {
+        return (mSystemUiStateFlags & SYSUI_STATE_NAV_BAR_HIDDEN) == 0
+                && (mSystemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) == 0
+                && ((mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) == 0
+                        || (mSystemUiStateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0);
+    }
+
+    private boolean canTriggerAssistantAction(MotionEvent ev) {
+        return mAssistantAvailable
+                && !QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags)
+                && (mAssistantLeftRegion.contains(ev.getX(), ev.getY()) ||
+                    mAssistantRightRegion.contains(ev.getX(), ev.getY()))
+                && !ActivityManagerWrapper.getInstance().isLockToAppActive();
+    }
+
+    private InputConsumer newConsumer(boolean useSharedState, MotionEvent event) {
+        boolean isInValidSystemUiState = validSystemUiFlags();
+
+        if (!mIsUserUnlocked) {
+            if (isInValidSystemUiState) {
+                // This handles apps launched in direct boot mode (e.g. dialer) as well as apps
+                // launched while device is locked even after exiting direct boot mode (e.g. camera).
+                return createDeviceLockedInputConsumer(mAM.getRunningTask(0));
+            } else {
+                return mResetGestureInputConsumer;
+            }
+        }
+
+        // When using sharedState, bypass systemState check as this is a followup gesture and the
+        // first gesture started in a valid system state.
+        InputConsumer base = isInValidSystemUiState || useSharedState
+                ? newBaseConsumer(useSharedState, event) : mResetGestureInputConsumer;
+        if (mMode == Mode.NO_BUTTON) {
+            final ActivityControlHelper activityControl =
+                    mOverviewComponentObserver.getActivityControlHelper();
+            if (canTriggerAssistantAction(event)) {
+                base = new AssistantTouchConsumer(this, mISystemUiProxy, activityControl, base,
+                        mInputMonitorCompat);
+            }
+
+            if ((mSystemUiStateFlags & SYSUI_STATE_SCREEN_PINNING) != 0) {
+                // Note: we only allow accessibility to wrap this, and it replaces the previous
+                // base input consumer (which should be NO_OP anyway since topTaskLocked == true).
+                base = new ScreenPinnedInputConsumer(this, mISystemUiProxy, activityControl);
+            }
+
+            if ((mSystemUiStateFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0) {
+                base = new AccessibilityInputConsumer(this, mISystemUiProxy,
+                        (mSystemUiStateFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0, base,
+                        mInputMonitorCompat, mSwipeTouchRegion);
+            }
+        } else {
+            if ((mSystemUiStateFlags & SYSUI_STATE_SCREEN_PINNING) != 0) {
+                base = mResetGestureInputConsumer;
+            }
+        }
+        return base;
+    }
+
+    private InputConsumer newBaseConsumer(boolean useSharedState, MotionEvent event) {
+        final RunningTaskInfo runningTaskInfo = mAM.getRunningTask(0);
+        if (!useSharedState) {
+            sSwipeSharedState.clearAllState(false /* finishAnimation */);
+        }
+        if ((mSystemUiStateFlags & SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED) != 0) {
+            // This handles apps showing over the lockscreen (e.g. camera)
+            return createDeviceLockedInputConsumer(runningTaskInfo);
+        }
+
+        final ActivityControlHelper activityControl =
+                mOverviewComponentObserver.getActivityControlHelper();
+
+        if (runningTaskInfo == null && !sSwipeSharedState.goingToLauncher
+                && !sSwipeSharedState.recentsAnimationFinishInterrupted) {
+            return mResetGestureInputConsumer;
+        } else if (sSwipeSharedState.recentsAnimationFinishInterrupted) {
+            // If the finish animation was interrupted, then continue using the other activity input
+            // consumer but with the next task as the running task
+            RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
+            info.id = sSwipeSharedState.nextRunningTaskId;
+            return createOtherActivityInputConsumer(event, info);
+        } else if (sSwipeSharedState.goingToLauncher || activityControl.isResumed()) {
+            return createOverviewInputConsumer(event);
+        } else if (ENABLE_QUICKSTEP_LIVE_TILE.get() && activityControl.isInLiveTileMode()) {
+            return createOverviewInputConsumer(event);
+        } else if (mGestureBlockingActivity != null && runningTaskInfo != null
+                && mGestureBlockingActivity.equals(runningTaskInfo.topActivity)) {
+            return mResetGestureInputConsumer;
+        } else if (mMode == Mode.NO_BUTTON && !mOverviewComponentObserver.isHomeAndOverviewSame()) {
+            return new FallbackNoButtonInputConsumer(this, activityControl,
+                    mInputMonitorCompat, sSwipeSharedState, mSwipeTouchRegion,
+                    mOverviewComponentObserver, disableHorizontalSwipe(event), runningTaskInfo);
+        } else {
+            return createOtherActivityInputConsumer(event, runningTaskInfo);
+        }
+    }
+
+    private boolean disableHorizontalSwipe(MotionEvent event) {
+        // mExclusionRegion can change on binder thread, use a local instance here.
+        Region exclusionRegion = mExclusionRegion;
+        return mMode == Mode.NO_BUTTON && exclusionRegion != null
+                && exclusionRegion.contains((int) event.getX(), (int) event.getY());
+    }
+
+    private OtherActivityInputConsumer createOtherActivityInputConsumer(MotionEvent event,
+            RunningTaskInfo runningTaskInfo) {
+        final ActivityControlHelper activityControl =
+                mOverviewComponentObserver.getActivityControlHelper();
+        boolean shouldDefer = activityControl.deferStartingActivity(mActiveNavBarRegion, event);
+
+        return new OtherActivityInputConsumer(this, runningTaskInfo, mRecentsModel,
+                mOverviewComponentObserver.getOverviewIntent(), activityControl,
+                shouldDefer, mOverviewCallbacks, mInputConsumer, this::onConsumerInactive,
+                sSwipeSharedState, mInputMonitorCompat, mSwipeTouchRegion,
+                disableHorizontalSwipe(event));
+    }
+
+    private InputConsumer createDeviceLockedInputConsumer(RunningTaskInfo taskInfo) {
+        if (mMode == Mode.NO_BUTTON && taskInfo != null) {
+            return new DeviceLockedInputConsumer(this, sSwipeSharedState, mInputMonitorCompat,
+                    mSwipeTouchRegion, taskInfo.taskId);
+        } else {
+            return mResetGestureInputConsumer;
+        }
+    }
+
+    public InputConsumer createOverviewInputConsumer(MotionEvent event) {
+        final ActivityControlHelper activityControl =
+                mOverviewComponentObserver.getActivityControlHelper();
+        BaseDraggingActivity activity = activityControl.getCreatedActivity();
+        if (activity == null) {
+            return mResetGestureInputConsumer;
+        }
+
+        if (activity.getRootView().hasWindowFocus() || sSwipeSharedState.goingToLauncher) {
+            return new OverviewInputConsumer(activity, mInputMonitorCompat,
+                    false /* startingInActivityBounds */);
+        } else {
+            return new OverviewWithoutFocusInputConsumer(this, mInputMonitorCompat,
+                    disableHorizontalSwipe(event));
+        }
+    }
+
+    /**
+     * To be called by the consumer when it's no longer active.
+     */
+    private void onConsumerInactive(InputConsumer caller) {
+        if (mConsumer == caller) {
+            mConsumer = mResetGestureInputConsumer;
+            mUncheckedConsumer = mConsumer;
+        }
+    }
+
+    @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()) {
+                case "cmd":
+                    if (args.peekArg() == null) {
+                        printAvailableCommands(pw);
+                    } else {
+                        onCommand(pw, args);
+                    }
+                    break;
+            }
+        } else {
+            // Dump everything
+            pw.println("TouchState:");
+            pw.println("  navMode=" + mMode);
+            pw.println("  validSystemUiFlags=" + validSystemUiFlags());
+            pw.println("  systemUiFlags=" + mSystemUiStateFlags);
+            pw.println("  systemUiFlagsDesc="
+                    + QuickStepContract.getSystemUiStateString(mSystemUiStateFlags));
+            pw.println("  assistantAvailable=" + mAssistantAvailable);
+            pw.println("  assistantDisabled="
+                    + QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags));
+            pw.println("  resumed="
+                    + mOverviewComponentObserver.getActivityControlHelper().isResumed());
+            pw.println("  useSharedState=" + mConsumer.useSharedSwipeState());
+            if (mConsumer.useSharedSwipeState()) {
+                sSwipeSharedState.dump("    ", pw);
+            }
+            pw.println("  mConsumer=" + mConsumer.getName());
+            pw.println("FeatureFlags:");
+            pw.println("  APPLY_CONFIG_AT_RUNTIME=" + APPLY_CONFIG_AT_RUNTIME.get());
+            pw.println("  QUICKSTEP_SPRINGS=" + QUICKSTEP_SPRINGS.get());
+            pw.println("  ADAPTIVE_ICON_WINDOW_ANIM=" + ADAPTIVE_ICON_WINDOW_ANIM.get());
+            pw.println("  ENABLE_QUICKSTEP_LIVE_TILE=" + ENABLE_QUICKSTEP_LIVE_TILE.get());
+            pw.println("  ENABLE_HINTS_IN_OVERVIEW=" + ENABLE_HINTS_IN_OVERVIEW.get());
+            pw.println("  FAKE_LANDSCAPE_UI=" + FAKE_LANDSCAPE_UI.get());
+            TOUCH_INTERACTION_LOG.dump("", pw);
+
+        }
+    }
+
+    private void printAvailableCommands(PrintWriter pw) {
+        pw.println("Available commands:");
+        pw.println("  clear-touch-log: Clears the touch interaction log");
+    }
+
+    private void onCommand(PrintWriter pw, ArgList args) {
+        switch (args.nextArg()) {
+            case "clear-touch-log":
+                TOUCH_INTERACTION_LOG.clear();
+                break;
+        }
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
new file mode 100644
index 0000000..476bb8f
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -0,0 +1,1481 @@
+/*
+ * 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.Utilities.SINGLE_FRAME_MS;
+import static com.android.launcher3.Utilities.postAsyncCallback;
+import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
+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.config.FeatureFlags.QUICKSTEP_SPRINGS;
+import static com.android.launcher3.util.RaceConditionTracker.ENTER;
+import static com.android.launcher3.util.RaceConditionTracker.EXIT;
+import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
+import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
+import static com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState.HIDE;
+import static com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState.PEEK;
+import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
+import static com.android.quickstep.TouchInteractionService.MAIN_THREAD_EXECUTOR;
+import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG;
+import static com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget.HOME;
+import static com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget.LAST_TASK;
+import static com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget.NEW_TASK;
+import static com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget.RECENTS;
+import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.HapticFeedbackConstants;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnApplyWindowInsetsListener;
+import android.view.ViewTreeObserver.OnDrawListener;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.view.animation.Interpolator;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile;
+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.Interpolators;
+import com.android.launcher3.graphics.RotationMode;
+import com.android.launcher3.logging.UserEventDispatcher;
+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.RaceConditionTracker;
+import com.android.launcher3.util.TraceHelper;
+import com.android.launcher3.views.FloatingIconView;
+import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
+import com.android.quickstep.ActivityControlHelper.AnimationFactory;
+import com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState;
+import com.android.quickstep.ActivityControlHelper.HomeAnimationFactory;
+import com.android.quickstep.SysUINavigationMode.Mode;
+import com.android.quickstep.inputconsumers.InputConsumer;
+import com.android.quickstep.inputconsumers.OverviewInputConsumer;
+import com.android.quickstep.util.ClipAnimationHelper;
+import com.android.quickstep.util.RectFSpringAnim;
+import com.android.quickstep.util.RemoteAnimationTargetSet;
+import com.android.quickstep.util.SwipeAnimationTargetSet;
+import com.android.quickstep.util.SwipeAnimationTargetSet.SwipeAnimationListener;
+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.InputConsumerController;
+import com.android.systemui.shared.system.LatencyTrackerCompat;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
+import com.android.systemui.shared.system.WindowCallbacksCompat;
+
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+
+@TargetApi(Build.VERSION_CODES.O)
+public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
+        implements SwipeAnimationListener, OnApplyWindowInsetsListener {
+    private static final String TAG = WindowTransformSwipeHandler.class.getSimpleName();
+
+    private static final Rect TEMP_RECT = new Rect();
+
+    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
+    private static final int STATE_LAUNCHER_PRESENT = getFlagForIndex(0, "STATE_LAUNCHER_PRESENT");
+    private static final int STATE_LAUNCHER_STARTED = getFlagForIndex(1, "STATE_LAUNCHER_STARTED");
+    private 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");
+
+    private 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");
+    private 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 enum GestureEndTarget {
+        HOME(1, STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT, true, false,
+                ContainerType.WORKSPACE, false),
+
+        RECENTS(1, STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT
+                | STATE_SCREENSHOT_VIEW_SHOWN, true, false, ContainerType.TASKSWITCHER, true),
+
+        NEW_TASK(0, STATE_START_NEW_TASK | STATE_CAPTURE_SCREENSHOT, false, true,
+                ContainerType.APP, true),
+
+        LAST_TASK(0, STATE_RESUME_LAST_TASK, false, true, ContainerType.APP, false);
+
+        GestureEndTarget(float endShift, int endState, boolean isLauncher, boolean canBeContinued,
+                int containerType, boolean recentsAttachedToAppWindow) {
+            this.endShift = endShift;
+            this.endState = endState;
+            this.isLauncher = isLauncher;
+            this.canBeContinued = canBeContinued;
+            this.containerType = containerType;
+            this.recentsAttachedToAppWindow = recentsAttachedToAppWindow;
+        }
+
+        /** 0 is app, 1 is overview */
+        public final float endShift;
+        /** The state to apply when we reach this final target */
+        public final int endState;
+        /** Whether the target is in the launcher activity */
+        public final boolean isLauncher;
+        /** Whether the user can start a new gesture while this one is finishing */
+        public final boolean canBeContinued;
+        /** Used to log where the user ended up after the gesture ends */
+        public final int containerType;
+        /** Whether RecentsView should be attached to the window as we animate to this target */
+        public final boolean recentsAttachedToAppWindow;
+    }
+
+    public static final long MAX_SWIPE_DURATION = 350;
+    public static final long MIN_SWIPE_DURATION = 80;
+    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";
+
+    private static final long SHELF_ANIM_DURATION = 240;
+    public static final long RECENTS_ATTACH_DURATION = 300;
+
+    // Start resisting when swiping past this factor of mTransitionDragLength.
+    private static final float DRAG_LENGTH_FACTOR_START_PULLBACK = 1.4f;
+    // This is how far down we can scale down, where 0f is full screen and 1f is recents.
+    private static final float DRAG_LENGTH_FACTOR_MAX_PULLBACK = 1.8f;
+    private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL;
+
+    /**
+     * 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;
+
+    private final ClipAnimationHelper mClipAnimationHelper;
+    private final ClipAnimationHelper.TransformParams mTransformParams;
+
+    private Runnable mGestureEndCallback;
+    private GestureEndTarget mGestureEndTarget;
+    // Either RectFSpringAnim (if animating home) or ObjectAnimator (from mCurrentShift) otherwise
+    private RunningWindowAnim mRunningWindowAnim;
+    private boolean mIsShelfPeeking;
+    private DeviceProfile mDp;
+    // The distance needed to drag to reach the task size in recents.
+    private int mTransitionDragLength;
+    // How much further we can drag past recents, as a factor of mTransitionDragLength.
+    private float mDragLengthFactor = 1;
+
+    // Shift in the range of [0, 1].
+    // 0 => preview snapShot is completely visible, and hotseat is completely translated down
+    // 1 => preview snapShot is completely aligned with the recents view and hotseat is completely
+    // visible.
+    private final AnimatedFloat mCurrentShift = new AnimatedFloat(this::updateFinalShift);
+    private boolean mContinuingLastGesture;
+    // To avoid UI jump when gesture is started, we offset the animation by the threshold.
+    private float mShiftAtGestureStart = 0;
+
+    private final Handler mMainThreadHandler = MAIN_THREAD_EXECUTOR.getHandler();
+
+    private final Context mContext;
+    private final ActivityControlHelper<T> mActivityControlHelper;
+    private final ActivityInitListener mActivityInitListener;
+
+    private final SysUINavigationMode.Mode mMode;
+
+    private final int mRunningTaskId;
+    private ThumbnailData mTaskSnapshot;
+
+    private MultiStateCallback mStateCallback;
+    // Used to control launcher components throughout the swipe gesture.
+    private AnimatorPlaybackController mLauncherTransitionController;
+    private boolean mHasLauncherTransitionControllerStarted;
+
+    private T mActivity;
+    private RecentsView mRecentsView;
+    private AnimationFactory mAnimationFactory = (t) -> { };
+    private LiveTileOverlay mLiveTileOverlay = new LiveTileOverlay();
+
+    private boolean mCanceled;
+    private boolean mWasLauncherAlreadyVisible;
+    private int mFinishingRecentsAnimationForNewTaskId = -1;
+
+    private boolean mPassedOverviewThreshold;
+    private boolean mGestureStarted;
+    private int mLogAction = Touch.SWIPE;
+    private int mLogDirection = Direction.UP;
+    private PointF mDownPos;
+    private boolean mIsLikelyToStartNewTask;
+
+    private final RecentsAnimationWrapper mRecentsAnimationWrapper;
+
+    private final long mTouchTimeMs;
+    private long mLauncherFrameDrawnTime;
+
+    public WindowTransformSwipeHandler(RunningTaskInfo runningTaskInfo, Context context,
+            long touchTimeMs, ActivityControlHelper<T> controller, boolean continuingLastGesture,
+            InputConsumerController inputConsumer) {
+        mContext = context;
+        mRunningTaskId = runningTaskInfo.id;
+        mTouchTimeMs = touchTimeMs;
+        mActivityControlHelper = controller;
+        mActivityInitListener = mActivityControlHelper
+                .createActivityInitListener(this::onActivityInit);
+        mContinuingLastGesture = continuingLastGesture;
+        mRecentsAnimationWrapper = new RecentsAnimationWrapper(inputConsumer,
+                this::createNewInputProxyHandler);
+        mClipAnimationHelper = new ClipAnimationHelper(context);
+        mTransformParams = new ClipAnimationHelper.TransformParams();
+
+        mMode = SysUINavigationMode.getMode(context);
+        initStateCallbacks();
+
+        DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext).getDeviceProfile(mContext);
+        initTransitionEndpoints(dp);
+    }
+
+    private void initStateCallbacks() {
+        mStateCallback = new MultiStateCallback(STATE_NAMES);
+
+        mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_GESTURE_STARTED,
+                this::onLauncherPresentAndGestureStarted);
+
+        mStateCallback.addCallback(STATE_LAUNCHER_DRAWN | STATE_GESTURE_STARTED,
+                this::initializeLauncherAnimationController);
+
+        mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN,
+                this::launcherFrameDrawn);
+
+        mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_STARTED
+                        | STATE_GESTURE_CANCELLED,
+                this::resetStateForAnimationCancel);
+
+        mStateCallback.addCallback(STATE_LAUNCHER_STARTED | STATE_APP_CONTROLLER_RECEIVED,
+                this::sendRemoteAnimationsToAnimationFactory);
+
+        mStateCallback.addCallback(STATE_RESUME_LAST_TASK | STATE_APP_CONTROLLER_RECEIVED,
+                this::resumeLastTask);
+        mStateCallback.addCallback(STATE_START_NEW_TASK | STATE_SCREENSHOT_CAPTURED,
+                this::startNewTask);
+
+        mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
+                        | STATE_LAUNCHER_DRAWN | STATE_CAPTURE_SCREENSHOT,
+                this::switchToScreenshot);
+
+        mStateCallback.addCallback(STATE_SCREENSHOT_CAPTURED | STATE_GESTURE_COMPLETED
+                        | STATE_SCALED_CONTROLLER_RECENTS,
+                this::finishCurrentTransitionToRecents);
+
+        mStateCallback.addCallback(STATE_SCREENSHOT_CAPTURED | STATE_GESTURE_COMPLETED
+                        | STATE_SCALED_CONTROLLER_HOME,
+                this::finishCurrentTransitionToHome);
+        mStateCallback.addCallback(STATE_SCALED_CONTROLLER_HOME | STATE_CURRENT_TASK_FINISHED,
+                this::reset);
+
+        mStateCallback.addCallback(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);
+
+        mStateCallback.addCallback(STATE_HANDLER_INVALIDATED, this::invalidateHandler);
+        mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
+                this::invalidateHandlerWithLauncher);
+        mStateCallback.addCallback(STATE_HANDLER_INVALIDATED | STATE_RESUME_LAST_TASK,
+                this::notifyTransitionCancelled);
+
+        mStateCallback.addCallback(STATE_APP_CONTROLLER_RECEIVED | STATE_GESTURE_STARTED,
+                mRecentsAnimationWrapper::enableInputConsumer);
+
+        if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+            mStateCallback.addChangeHandler(STATE_APP_CONTROLLER_RECEIVED | STATE_LAUNCHER_PRESENT
+                            | STATE_SCREENSHOT_VIEW_SHOWN | STATE_CAPTURE_SCREENSHOT,
+                    (b) -> mRecentsView.setRunningTaskHidden(!b));
+        }
+    }
+
+    private void setStateOnUiThread(int stateFlag) {
+        if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
+            mStateCallback.setState(stateFlag);
+        } else {
+            postAsyncCallback(mMainThreadHandler, () -> mStateCallback.setState(stateFlag));
+        }
+    }
+
+    private void initTransitionEndpoints(DeviceProfile dp) {
+        mDp = dp;
+
+        Rect tempRect = new Rect();
+        mTransitionDragLength = mActivityControlHelper.getSwipeUpDestinationAndLength(
+                dp, mContext, tempRect);
+        mClipAnimationHelper.updateTargetRect(tempRect);
+        if (mMode == Mode.NO_BUTTON) {
+            // We can drag all the way to the top of the screen.
+            mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength;
+        }
+    }
+
+    private long getFadeInDuration() {
+        if (mCurrentShift.getCurrentAnimation() != null) {
+            ObjectAnimator anim = mCurrentShift.getCurrentAnimation();
+            long theirDuration = anim.getDuration() - anim.getCurrentPlayTime();
+
+            // TODO: Find a better heuristic
+            return Math.min(MAX_SWIPE_DURATION, Math.max(theirDuration, MIN_SWIPE_DURATION));
+        } else {
+            return MAX_SWIPE_DURATION;
+        }
+    }
+
+    public void initWhenReady() {
+        mActivityInitListener.register();
+    }
+
+    private boolean onActivityInit(final T activity, Boolean alreadyOnHome) {
+        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();
+        SyncRtSurfaceTransactionApplierCompat.create(mRecentsView, applier -> {
+            mTransformParams.setSyncTransactionApplier(applier);
+            mRecentsAnimationWrapper.runOnInit(() ->
+                    mRecentsAnimationWrapper.targetSet.addDependentTransactionApplier(applier));
+            });
+
+        mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
+            if (mGestureEndTarget != HOME) {
+                updateFinalShift();
+            }
+        });
+        mRecentsView.setRecentsAnimationWrapper(mRecentsAnimationWrapper);
+        mRecentsView.setClipAnimationHelper(mClipAnimationHelper);
+        mRecentsView.setLiveTileOverlay(mLiveTileOverlay);
+        mActivity.getRootView().getOverlay().add(mLiveTileOverlay);
+
+        mStateCallback.setState(STATE_LAUNCHER_PRESENT);
+        if (alreadyOnHome) {
+            onLauncherStart(activity);
+        } else {
+            activity.setOnStartCallback(this::onLauncherStart);
+        }
+
+        setupRecentsViewUi();
+        return true;
+    }
+
+    private void onLauncherStart(final T activity) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_OVERVIEW_EVENT_TAG, "onLauncherStart");
+        }
+        if (mActivity != activity) {
+            return;
+        }
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_OVERVIEW_EVENT_TAG, "onLauncherStart 1");
+        }
+        if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
+            return;
+        }
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_OVERVIEW_EVENT_TAG, "onLauncherStart 2");
+        }
+
+        // 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 (mGestureEndTarget != HOME) {
+            if (TestProtocol.sDebugTracing) {
+                Log.d(TestProtocol.NO_OVERVIEW_EVENT_TAG, "onLauncherStart 3");
+            }
+            Runnable initAnimFactory = () -> {
+                if (TestProtocol.sDebugTracing) {
+                    Log.d(TestProtocol.NO_OVERVIEW_EVENT_TAG, "onLauncherStart 4");
+                }
+                mAnimationFactory = mActivityControlHelper.prepareRecentsUI(mActivity,
+                        mWasLauncherAlreadyVisible, true,
+                        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.
+                if (TestProtocol.sDebugTracing) {
+                    Log.d(TestProtocol.NO_OVERVIEW_EVENT_TAG, "onLauncherStart 5");
+                }
+                mStateCallback.addCallback(STATE_GESTURE_STARTED, initAnimFactory);
+            } else {
+                if (TestProtocol.sDebugTracing) {
+                    Log.d(TestProtocol.NO_OVERVIEW_EVENT_TAG, "onLauncherStart 6");
+                }
+                initAnimFactory.run();
+            }
+        }
+        AbstractFloatingView.closeAllOpenViewsExcept(activity, mWasLauncherAlreadyVisible,
+                AbstractFloatingView.TYPE_LISTENER);
+
+        if (mWasLauncherAlreadyVisible) {
+            mStateCallback.setState(STATE_LAUNCHER_DRAWN);
+        } else {
+            TraceHelper.beginSection("WTS-init");
+            View dragLayer = activity.getDragLayer();
+            dragLayer.getViewTreeObserver().addOnDrawListener(new OnDrawListener() {
+
+                @Override
+                public void onDraw() {
+                    TraceHelper.endSection("WTS-init", "Launcher frame is drawn");
+                    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();
+
+        notifyGestureStartedAsync();
+    }
+
+    private void setupRecentsViewUi() {
+        if (mContinuingLastGesture) {
+            updateSysUiFlags(mCurrentShift.value);
+            return;
+        }
+        mRecentsView.onGestureAnimationStart(mRunningTaskId);
+    }
+
+    private void launcherFrameDrawn() {
+        mLauncherFrameDrawnTime = SystemClock.uptimeMillis();
+    }
+
+    private void sendRemoteAnimationsToAnimationFactory() {
+        mAnimationFactory.onRemoteAnimationReceived(mRecentsAnimationWrapper.targetSet);
+    }
+
+    private void initializeLauncherAnimationController() {
+        buildAnimationController();
+
+        if (LatencyTrackerCompat.isEnabled(mContext)) {
+            LatencyTrackerCompat.logToggleRecents((int) (mLauncherFrameDrawnTime - mTouchTimeMs));
+        }
+
+        // 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);
+    }
+
+    private float getTaskCurveScaleForOffsetX(float offsetX, float taskWidth) {
+        float distanceToReachEdge = mDp.widthPx / 2 + taskWidth / 2 +
+                mContext.getResources().getDimensionPixelSize(R.dimen.recents_page_spacing);
+        float interpolation = Math.min(1, offsetX / distanceToReachEdge);
+        return TaskView.getCurveScaleForInterpolation(interpolation);
+    }
+
+    public Consumer<MotionEvent> getRecentsViewDispatcher(RotationMode rotationMode) {
+        return mRecentsView != null ? mRecentsView.getEventDispatcher(rotationMode) : null;
+    }
+
+    @UiThread
+    public void updateDisplacement(float displacement) {
+        // We are moving in the negative x/y direction
+        displacement = -displacement;
+        if (displacement > mTransitionDragLength * mDragLengthFactor && mTransitionDragLength > 0) {
+            mCurrentShift.updateValue(mDragLengthFactor);
+        } else {
+            float translation = Math.max(displacement, 0);
+            float shift = mTransitionDragLength == 0 ? 0 : translation / mTransitionDragLength;
+            if (shift > DRAG_LENGTH_FACTOR_START_PULLBACK) {
+                float pullbackProgress = Utilities.getProgress(shift,
+                        DRAG_LENGTH_FACTOR_START_PULLBACK, mDragLengthFactor);
+                pullbackProgress = PULLBACK_INTERPOLATOR.getInterpolation(pullbackProgress);
+                shift = DRAG_LENGTH_FACTOR_START_PULLBACK + pullbackProgress
+                        * (DRAG_LENGTH_FACTOR_MAX_PULLBACK - DRAG_LENGTH_FACTOR_START_PULLBACK);
+            }
+            mCurrentShift.updateValue(shift);
+        }
+    }
+
+    public void onMotionPauseChanged(boolean isPaused) {
+        setShelfState(isPaused ? PEEK : HIDE, OVERSHOOT_1_2, SHELF_ANIM_DURATION);
+    }
+
+    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 (mMode != Mode.NO_BUTTON || mRecentsView == null) {
+            return;
+        }
+        RemoteAnimationTargetCompat runningTaskTarget = mRecentsAnimationWrapper.targetSet == null
+                ? null
+                : mRecentsAnimationWrapper.targetSet.findTask(mRunningTaskId);
+        final boolean recentsAttachedToAppWindow;
+        int runningTaskIndex = mRecentsView.getRunningTaskIndex();
+        if (mGestureEndTarget != null) {
+            recentsAttachedToAppWindow = mGestureEndTarget.recentsAttachedToAppWindow;
+        } else if (mContinuingLastGesture
+                && mRecentsView.getRunningTaskIndex() != mRecentsView.getNextPage()) {
+            recentsAttachedToAppWindow = true;
+            animate = false;
+        } else if (runningTaskTarget != null && isNotInRecents(runningTaskTarget)) {
+            // The window is going away so make sure recents is always visible in this case.
+            recentsAttachedToAppWindow = true;
+            animate = false;
+        } else {
+            recentsAttachedToAppWindow = mIsShelfPeeking || mIsLikelyToStartNewTask;
+            if (animate) {
+                // Only animate if an adjacent task view is visible on screen.
+                TaskView adjacentTask1 = mRecentsView.getTaskViewAt(runningTaskIndex + 1);
+                TaskView adjacentTask2 = mRecentsView.getTaskViewAt(runningTaskIndex - 1);
+                float prevTranslationX = mRecentsView.getTranslationX();
+                mRecentsView.setTranslationX(0);
+                animate = (adjacentTask1 != null && adjacentTask1.getGlobalVisibleRect(TEMP_RECT))
+                        || (adjacentTask2 != null && adjacentTask2.getGlobalVisibleRect(TEMP_RECT));
+                mRecentsView.setTranslationX(prevTranslationX);
+            }
+        }
+        mAnimationFactory.setRecentsAttachedToAppWindow(recentsAttachedToAppWindow, animate);
+    }
+
+    public void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask) {
+        if (mIsLikelyToStartNewTask != isLikelyToStartNewTask) {
+            mIsLikelyToStartNewTask = isLikelyToStartNewTask;
+            maybeUpdateRecentsAttachedState();
+        }
+    }
+
+    @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 (mRecentsView != null && shelfState.shouldPreformHaptic) {
+            mRecentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
+                    HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+        }
+    }
+
+    private void buildAnimationController() {
+        if (mGestureEndTarget == HOME || mHasLauncherTransitionControllerStarted) {
+            // We don't want a new mLauncherTransitionController if mGestureEndTarget == HOME (it
+            // has its own animation) or if we're already animating the current controller.
+            return;
+        }
+        initTransitionEndpoints(mActivity.getDeviceProfile());
+        mAnimationFactory.createActivityController(mTransitionDragLength);
+    }
+
+    @Override
+    public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) {
+        WindowInsets result = view.onApplyWindowInsets(windowInsets);
+        buildAnimationController();
+        return result;
+    }
+
+    private void onAnimatorPlaybackControllerCreated(AnimatorPlaybackController anim) {
+        mLauncherTransitionController = anim;
+        mLauncherTransitionController.dispatchSetInterpolator(t -> t * mDragLengthFactor);
+        mAnimationFactory.adjustActivityControllerInterpolators();
+        mLauncherTransitionController.dispatchOnStart();
+        updateLauncherTransitionProgress();
+    }
+
+    @UiThread
+    private void updateFinalShift() {
+        float shift = mCurrentShift.value;
+
+        SwipeAnimationTargetSet controller = mRecentsAnimationWrapper.getController();
+        if (controller != null) {
+            float offsetX = mRecentsView == null ? 0 : mRecentsView.getScrollOffset();
+            float offsetScale = getTaskCurveScaleForOffsetX(offsetX,
+                    mClipAnimationHelper.getTargetRect().width());
+            mTransformParams.setProgress(shift).setOffsetX(offsetX).setOffsetScale(offsetScale);
+            mClipAnimationHelper.applyTransform(mRecentsAnimationWrapper.targetSet,
+                    mTransformParams);
+            updateSysUiFlags(shift);
+        }
+
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+            if (mRecentsAnimationWrapper.getController() != null) {
+                mLiveTileOverlay.update(mClipAnimationHelper.getCurrentRectWithInsets(),
+                        mClipAnimationHelper.getCurrentCornerRadius());
+            }
+        }
+
+        final boolean passed = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW;
+        if (passed != mPassedOverviewThreshold) {
+            mPassedOverviewThreshold = passed;
+            if (mRecentsView != null && mMode != Mode.NO_BUTTON) {
+                mRecentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
+                    HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+            }
+        }
+
+        if (mLauncherTransitionController == null || mLauncherTransitionController
+                .getAnimationPlayer().isStarted()) {
+            return;
+        }
+        updateLauncherTransitionProgress();
+    }
+
+    private void updateLauncherTransitionProgress() {
+        if (mGestureEndTarget == HOME) {
+            return;
+        }
+        // Normalize the progress to 0 to 1, as the animation controller will clamp it to that
+        // anyway. The controller mimics the drag length factor by applying it to its interpolators.
+        float progress = mCurrentShift.value / mDragLengthFactor;
+        mLauncherTransitionController.setPlayFraction(
+                progress <= mShiftAtGestureStart || mShiftAtGestureStart >= 1
+                        ? 0 : (progress - mShiftAtGestureStart) / (1 - mShiftAtGestureStart));
+    }
+
+    /**
+     * @param windowProgress 0 == app, 1 == overview
+     */
+    private void updateSysUiFlags(float windowProgress) {
+        if (mRecentsView != null) {
+            TaskView centermostTask = mRecentsView.getTaskViewAt(mRecentsView
+                    .getPageNearestToCenterOfScreen());
+            int centermostTaskFlags = centermostTask == null ? 0
+                    : centermostTask.getThumbnail().getSysUiStatusNavFlags();
+            boolean useHomeScreenFlags = windowProgress > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD;
+            // We will handle the sysui flags based on the centermost task view.
+            mRecentsAnimationWrapper.setWindowThresholdCrossed(centermostTaskFlags != 0
+                    || useHomeScreenFlags);
+            int sysuiFlags = useHomeScreenFlags ? 0 : centermostTaskFlags;
+            mActivity.getSystemUiController().updateUiState(UI_STATE_OVERVIEW, sysuiFlags);
+        }
+    }
+
+    @Override
+    public void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet) {
+        DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext).getDeviceProfile(mContext);
+        final Rect overviewStackBounds;
+        RemoteAnimationTargetCompat runningTaskTarget = targetSet.findTask(mRunningTaskId);
+
+        if (targetSet.minimizedHomeBounds != null && runningTaskTarget != null) {
+            overviewStackBounds = mActivityControlHelper
+                    .getOverviewWindowBounds(targetSet.minimizedHomeBounds, runningTaskTarget);
+            dp = dp.getMultiWindowProfile(mContext, new Point(
+                    targetSet.minimizedHomeBounds.width(), targetSet.minimizedHomeBounds.height()));
+            dp.updateInsets(targetSet.homeContentInsets);
+        } else {
+            if (mActivity != null) {
+                int loc[] = new int[2];
+                View rootView = mActivity.getRootView();
+                rootView.getLocationOnScreen(loc);
+                overviewStackBounds = new Rect(loc[0], loc[1], loc[0] + rootView.getWidth(),
+                        loc[1] + rootView.getHeight());
+            } else {
+                overviewStackBounds = new Rect(0, 0, dp.widthPx, dp.heightPx);
+            }
+            // If we are not in multi-window mode, home insets should be same as system insets.
+            dp = dp.copy(mContext);
+            dp.updateInsets(targetSet.homeContentInsets);
+        }
+        dp.updateIsSeascape(mContext.getSystemService(WindowManager.class));
+
+        if (runningTaskTarget != null) {
+            mClipAnimationHelper.updateSource(overviewStackBounds, runningTaskTarget);
+        }
+        mClipAnimationHelper.prepareAnimation(dp, false /* isOpening */);
+        initTransitionEndpoints(dp);
+
+        mRecentsAnimationWrapper.setController(targetSet);
+        TOUCH_INTERACTION_LOG.addLog("startRecentsAnimationCallback", targetSet.apps.length);
+        setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
+
+        mPassedOverviewThreshold = false;
+    }
+
+    @Override
+    public void onRecentsAnimationCanceled() {
+        mRecentsAnimationWrapper.setController(null);
+        mActivityInitListener.unregister();
+        setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED);
+        TOUCH_INTERACTION_LOG.addLog("cancelRecentsAnimation");
+    }
+
+    @UiThread
+    public void onGestureStarted() {
+        notifyGestureStartedAsync();
+        mShiftAtGestureStart = mCurrentShift.value;
+        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.
+     */
+    @UiThread
+    public void onGestureCancelled() {
+        updateDisplacement(0);
+        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.
+     */
+    @UiThread
+    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;
+        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 */);
+    }
+
+    @UiThread
+    private InputConsumer createNewInputProxyHandler() {
+        endRunningWindowAnim();
+        endLauncherTransitionController();
+        if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+            // Hide the task view, if not already hidden
+            setTargetAlphaProvider(WindowTransformSwipeHandler::getHiddenTargetAlpha);
+        }
+
+        BaseDraggingActivity activity = mActivityControlHelper.getCreatedActivity();
+        return activity == null
+                ? InputConsumer.NO_OP : new OverviewInputConsumer(activity, null, true);
+    }
+
+    private void endRunningWindowAnim() {
+        if (mRunningWindowAnim != null) {
+            mRunningWindowAnim.end();
+        }
+    }
+
+    private GestureEndTarget calculateEndTarget(PointF velocity, float endVelocity, boolean isFling,
+            boolean isCancel) {
+        final GestureEndTarget endTarget;
+        final boolean goingToNewTask;
+        if (mRecentsView != null) {
+            if (!mRecentsAnimationWrapper.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 (mMode == Mode.NO_BUTTON) {
+                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 (mMode == Mode.NO_BUTTON && endVelocity < 0 && !mIsShelfPeeking) {
+                // If swiping at a diagonal, base end target on the faster velocity.
+                endTarget = goingToNewTask && Math.abs(velocity.x) > Math.abs(endVelocity)
+                        ? NEW_TASK : HOME;
+            } else if (endVelocity < 0) {
+                if (reachedOverviewThreshold) {
+                    endTarget = RECENTS;
+                } else {
+                    // If swiping at a diagonal, base end target on the faster velocity.
+                    endTarget = goingToNewTask && Math.abs(velocity.x) > Math.abs(endVelocity)
+                            ? NEW_TASK : RECENTS;
+                }
+            } else {
+                endTarget = goingToNewTask ? NEW_TASK : LAST_TASK;
+            }
+        }
+
+        int stateFlags = OverviewInteractionState.INSTANCE.get(mActivity).getSystemUiStateFlags();
+        if ((stateFlags & SYSUI_STATE_OVERVIEW_DISABLED) != 0
+                && (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.endShift;
+        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
+                    * SINGLE_FRAME_MS / mTransitionDragLength, 0, mDragLengthFactor);
+            float minFlingVelocity = mContext.getResources()
+                    .getDimension(R.dimen.quickstep_fling_min_velocity);
+            if (Math.abs(endVelocity) > minFlingVelocity && mTransitionDragLength > 0) {
+                if (endTarget == RECENTS && mMode != Mode.NO_BUTTON) {
+                    Interpolators.OvershootParams overshoot = new Interpolators.OvershootParams(
+                            startShift, endShift, endShift, velocityPxPerMs.y,
+                            mTransitionDragLength);
+                    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) {
+            mRecentsAnimationWrapper.enableInputProxy();
+        }
+
+        if (endTarget == HOME) {
+            setShelfState(ShelfAnimState.CANCEL, LINEAR, 0);
+            duration = Math.max(MIN_OVERSHOOT_DURATION, duration);
+        } else if (endTarget == RECENTS) {
+            mLiveTileOverlay.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 (mMode == Mode.NO_BUTTON) {
+                setShelfState(ShelfAnimState.OVERVIEW, interpolator, duration);
+            }
+        } else if (endTarget == NEW_TASK || endTarget == LAST_TASK) {
+            // Let RecentsView handle the scrolling to the task, which we launch in startNewTask()
+            // or resumeLastTask().
+            if (mRecentsView != null) {
+                duration = Math.max(duration, mRecentsView.getScroller().getDuration());
+            }
+        }
+        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);
+    }
+
+    /** 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) {
+        mRecentsAnimationWrapper.runOnInit(() -> animateToProgressInternal(start, end, duration,
+                interpolator, target, velocityPxPerMs));
+    }
+
+    @UiThread
+    private void animateToProgressInternal(float start, float end, long duration,
+            Interpolator interpolator, GestureEndTarget target, PointF velocityPxPerMs) {
+        mGestureEndTarget = target;
+
+        maybeUpdateRecentsAttachedState();
+
+        if (mGestureEndTarget == HOME) {
+            HomeAnimationFactory homeAnimFactory;
+            if (mActivity != null) {
+                homeAnimFactory = mActivityControlHelper.prepareHomeUI(mActivity);
+            } else {
+                homeAnimFactory = new HomeAnimationFactory() {
+                    @NonNull
+                    @Override
+                    public RectF getWindowTargetRect() {
+                        RectF fallbackTarget = new RectF(mClipAnimationHelper.getTargetRect());
+                        Utilities.scaleRectFAboutCenter(fallbackTarget, 0.25f);
+                        return fallbackTarget;
+                    }
+
+                    @NonNull
+                    @Override
+                    public AnimatorPlaybackController createActivityAnimationToHome() {
+                        return AnimatorPlaybackController.wrap(new AnimatorSet(), duration);
+                    }
+                };
+                mStateCallback.addChangeHandler(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
+                        isPresent -> mRecentsView.startHome());
+            }
+            RectFSpringAnim windowAnim = createWindowAnimationToHome(start, homeAnimFactory);
+            windowAnim.addAnimatorListener(new AnimationSuccessListener() {
+                @Override
+                public void onAnimationSuccess(Animator animator) {
+                    setStateOnUiThread(target.endState);
+                }
+            });
+            windowAnim.start(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 -> {
+                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();
+                }
+            });
+            windowAnim.addListener(new AnimationSuccessListener() {
+                @Override
+                public void onAnimationSuccess(Animator animator) {
+                    setStateOnUiThread(target.endState);
+                }
+            });
+            windowAnim.start();
+            mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim);
+        }
+        // Always play the entire launcher animation when going home, since it is separate from
+        // the animation that has been controlled thus far.
+        if (mGestureEndTarget == HOME) {
+            start = 0;
+        }
+
+        // We want to use the same interpolator as the window, but need to adjust it to
+        // interpolate over the remaining progress (end - start).
+        TimeInterpolator adjustedInterpolator = Interpolators.mapToProgress(
+                interpolator, start, end);
+        if (mLauncherTransitionController == null) {
+            return;
+        }
+        if (start == end || duration <= 0) {
+            mLauncherTransitionController.dispatchSetInterpolator(t -> end);
+        } else {
+            mLauncherTransitionController.dispatchSetInterpolator(adjustedInterpolator);
+            mAnimationFactory.adjustActivityControllerInterpolators();
+        }
+        mLauncherTransitionController.getAnimationPlayer().setDuration(Math.max(0, duration));
+
+        if (QUICKSTEP_SPRINGS.get()) {
+            mLauncherTransitionController.dispatchOnStartWithVelocity(end, velocityPxPerMs.y);
+        }
+        mLauncherTransitionController.getAnimationPlayer().start();
+        mHasLauncherTransitionControllerStarted = true;
+    }
+
+    /**
+     * 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.
+     */
+    private RectFSpringAnim createWindowAnimationToHome(float startProgress,
+            HomeAnimationFactory homeAnimationFactory) {
+        final RemoteAnimationTargetSet targetSet = mRecentsAnimationWrapper.targetSet;
+        final RectF startRect = new RectF(mClipAnimationHelper.applyTransform(targetSet,
+                mTransformParams.setProgress(startProgress), false /* launcherOnTop */));
+        final RectF targetRect = homeAnimationFactory.getWindowTargetRect();
+
+        final View floatingView = homeAnimationFactory.getFloatingView();
+        final boolean isFloatingIconView = floatingView instanceof FloatingIconView;
+        RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mActivity.getResources());
+        if (isFloatingIconView) {
+            FloatingIconView fiv = (FloatingIconView) floatingView;
+            anim.addAnimatorListener(fiv);
+            fiv.setOnTargetChangeListener(anim::onTargetPositionChanged);
+        }
+
+        AnimatorPlaybackController homeAnim = homeAnimationFactory.createActivityAnimationToHome();
+
+        // 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.
+        float startRadius = mClipAnimationHelper.getCurrentCornerRadius();
+        float endRadius = startRect.width() / 6f;
+        // We want the window alpha to be 0 once this threshold is met, so that the
+        // FolderIconView can be seen morphing into the icon shape.
+        final float windowAlphaThreshold = isFloatingIconView ? 1f - SHAPE_PROGRESS_DURATION : 1f;
+        anim.addOnUpdateListener((currentRect, progress) -> {
+            homeAnim.setPlayFraction(progress);
+
+            float alphaProgress = ACCEL_1_5.getInterpolation(progress);
+            float windowAlpha = Utilities.boundToRange(Utilities.mapToRange(alphaProgress, 0,
+                    windowAlphaThreshold, 1.5f, 0f, Interpolators.LINEAR), 0, 1);
+            mTransformParams.setProgress(progress)
+                    .setCurrentRectAndTargetAlpha(currentRect, windowAlpha);
+            if (isFloatingIconView) {
+                mTransformParams.setCornerRadius(endRadius * progress + startRadius
+                        * (1f - progress));
+            }
+            mClipAnimationHelper.applyTransform(targetSet, mTransformParams,
+                    false /* launcherOnTop */);
+
+            if (isFloatingIconView) {
+                ((FloatingIconView) floatingView).update(currentRect, 1f, progress,
+                        windowAlphaThreshold, mClipAnimationHelper.getCurrentCornerRadius(), false);
+            }
+
+            updateSysUiFlags(Math.max(progress, mCurrentShift.value));
+        });
+        anim.addAnimatorListener(new AnimationSuccessListener() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                homeAnim.dispatchOnStart();
+                if (mActivity != null) {
+                    mActivity.getRootView().getOverlay().remove(mLiveTileOverlay);
+                }
+            }
+
+            @Override
+            public void onAnimationSuccess(Animator animator) {
+                homeAnim.getAnimationPlayer().end();
+                if (mRecentsView != null) {
+                    mRecentsView.post(mRecentsView::resetTaskVisuals);
+                }
+                // Make sure recents is in its final state
+                maybeUpdateRecentsAttachedState(false);
+                mActivityControlHelper.onSwipeUpToHomeComplete(mActivity);
+            }
+        });
+        return anim;
+    }
+
+    /**
+     * @return The GestureEndTarget if the gesture has ended, else null.
+     */
+    public @Nullable GestureEndTarget getGestureEndTarget() {
+        return mGestureEndTarget;
+    }
+
+    @UiThread
+    private void resumeLastTask() {
+        mRecentsAnimationWrapper.finish(false /* toRecents */, null);
+        TOUCH_INTERACTION_LOG.addLog("finishRecentsAnimation", false);
+        doLogGesture(LAST_TASK);
+        reset();
+    }
+
+    @UiThread
+    private void startNewTask() {
+        // 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.getTaskViewAt(mRecentsView.getNextPage()).launchTask(false /* animate */,
+                    true /* freezeTaskList */);
+        } else {
+            int taskId = mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).getTask().key.id;
+            mFinishingRecentsAnimationForNewTaskId = taskId;
+            mRecentsAnimationWrapper.finish(true /* toRecents */, () -> {
+                if (!mCanceled) {
+                    TaskView nextTask = mRecentsView.getTaskView(taskId);
+                    if (nextTask != null) {
+                        nextTask.launchTask(false /* animate */, true /* freezeTaskList */,
+                                success -> {
+                            if (!success) {
+                                // 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();
+                                mActivityControlHelper.onLaunchTaskFailed(mActivity);
+                                nextTask.notifyTaskLaunchFailed(TAG);
+                                updateSysUiFlags(1 /* windowProgress == overview */);
+                            } else {
+                                mActivityControlHelper.onLaunchTaskSuccess(mActivity);
+                            }
+                        }, mMainThreadHandler);
+                        doLogGesture(NEW_TASK);
+                    }
+                    reset();
+                }
+                mCanceled = false;
+                mFinishingRecentsAnimationForNewTaskId = -1;
+            });
+        }
+        TOUCH_INTERACTION_LOG.addLog("finishRecentsAnimation", true);
+    }
+
+    public void reset() {
+        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).
+     */
+    public void cancelCurrentAnimation(SwipeSharedState sharedState) {
+        mCanceled = true;
+        mCurrentShift.cancelAnimation();
+        if (mLauncherTransitionController != null && mLauncherTransitionController
+                .getAnimationPlayer().isStarted()) {
+            mLauncherTransitionController.getAnimationPlayer().cancel();
+        }
+
+        if (mFinishingRecentsAnimationForNewTaskId != -1) {
+            // If we are canceling mid-starting a new task, switch to the screenshot since the
+            // recents animation has finished
+            switchToScreenshot();
+            TaskView newRunningTaskView = mRecentsView.getTaskView(
+                    mFinishingRecentsAnimationForNewTaskId);
+            int newRunningTaskId = newRunningTaskView != null
+                    ? newRunningTaskView.getTask().key.id
+                    : -1;
+            mRecentsView.setCurrentTask(newRunningTaskId);
+            sharedState.setRecentsAnimationFinishInterrupted(newRunningTaskId);
+        }
+    }
+
+    private void invalidateHandler() {
+        endRunningWindowAnim();
+
+        if (mGestureEndCallback != null) {
+            mGestureEndCallback.run();
+        }
+
+        mActivityInitListener.unregister();
+        mTaskSnapshot = null;
+    }
+
+    private void invalidateHandlerWithLauncher() {
+        endLauncherTransitionController();
+
+        mRecentsView.onGestureAnimationEnd();
+
+        mActivity.getRootView().setOnApplyWindowInsetsListener(null);
+        mActivity.getRootView().getOverlay().remove(mLiveTileOverlay);
+    }
+
+    private void endLauncherTransitionController() {
+        setShelfState(ShelfAnimState.CANCEL, LINEAR, 0);
+        if (mLauncherTransitionController != null) {
+            mLauncherTransitionController.getAnimationPlayer().end();
+            mLauncherTransitionController = null;
+        }
+    }
+
+    private void notifyTransitionCancelled() {
+        mAnimationFactory.onTransitionCancelled();
+    }
+
+    private void resetStateForAnimationCancel() {
+        boolean wasVisible = mWasLauncherAlreadyVisible || mGestureStarted;
+        mActivityControlHelper.onTransitionCancelled(mActivity, wasVisible);
+
+        // Leave the pending invisible flag, as it may be used by wallpaper open animation.
+        mActivity.clearForceInvisibleFlag(INVISIBLE_BY_STATE_HANDLER);
+    }
+
+    private void switchToScreenshot() {
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+            setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
+        } else if (!mRecentsAnimationWrapper.hasTargets()) {
+            // If there are no targets, then we don't need to capture anything
+            setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
+        } else {
+            boolean finishTransitionPosted = false;
+            SwipeAnimationTargetSet controller = mRecentsAnimationWrapper.getController();
+            if (controller != null) {
+                // Update the screenshot of the task
+                if (mTaskSnapshot == null) {
+                    mTaskSnapshot = controller.screenshotTask(mRunningTaskId);
+                }
+                final TaskView taskView;
+                if (mGestureEndTarget == 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(mRunningTaskId, mTaskSnapshot);
+                }
+                if (taskView != null && !mCanceled) {
+                    // Defer finishing the animation until the next launcher frame with the
+                    // new thumbnail
+                    finishTransitionPosted = new WindowCallbacksCompat(taskView) {
+
+                        // 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 (mCanceled) {
+                                detach();
+                                return;
+                            }
+
+                            if (mDeferFrameCount > 0) {
+                                mDeferFrameCount--;
+                                // Workaround, detach and reattach to invalidate the root node for
+                                // another draw
+                                detach();
+                                attach();
+                                taskView.invalidate();
+                                return;
+                            }
+
+                            setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
+                            detach();
+                        }
+                    }.attach();
+                }
+            }
+            if (!finishTransitionPosted) {
+                // If we haven't posted a draw callback, set the state immediately.
+                RaceConditionTracker.onEvent(SCREENSHOT_CAPTURED_EVT, ENTER);
+                setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
+                RaceConditionTracker.onEvent(SCREENSHOT_CAPTURED_EVT, EXIT);
+            }
+        }
+    }
+
+    private void finishCurrentTransitionToRecents() {
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+            setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
+        } else if (!mRecentsAnimationWrapper.hasTargets()) {
+            // If there are no targets, then there is nothing to finish
+            setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
+        } else {
+            synchronized (mRecentsAnimationWrapper) {
+                mRecentsAnimationWrapper.finish(true /* toRecents */,
+                        () -> setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
+            }
+        }
+        TOUCH_INTERACTION_LOG.addLog("finishRecentsAnimation", true);
+    }
+
+    private void finishCurrentTransitionToHome() {
+        synchronized (mRecentsAnimationWrapper) {
+            mRecentsAnimationWrapper.finish(true /* toRecents */,
+                    () -> setStateOnUiThread(STATE_CURRENT_TASK_FINISHED),
+                    true /* sendUserLeaveHint */);
+        }
+        TOUCH_INTERACTION_LOG.addLog("finishRecentsAnimation", true);
+        doLogGesture(HOME);
+    }
+
+    private void setupLauncherUiAfterSwipeUpToRecentsAnimation() {
+        endLauncherTransitionController();
+        mActivityControlHelper.onSwipeUpToRecentsComplete(mActivity);
+        mRecentsAnimationWrapper.setCancelWithDeferredScreenshot(true);
+        mRecentsView.onSwipeUpAnimationSuccess();
+
+        RecentsModel.INSTANCE.get(mContext).onOverviewShown(false, TAG);
+
+        doLogGesture(RECENTS);
+        reset();
+    }
+
+    public void setGestureEndCallback(Runnable gestureEndCallback) {
+        mGestureEndCallback = gestureEndCallback;
+    }
+
+    private void setTargetAlphaProvider(
+            BiFunction<RemoteAnimationTargetCompat, Float, Float> provider) {
+        mClipAnimationHelper.setTaskAlphaCallback(provider);
+        updateFinalShift();
+    }
+
+    public static float getHiddenTargetAlpha(RemoteAnimationTargetCompat app, Float expectedAlpha) {
+        if (!isNotInRecents(app)) {
+            return 0;
+        }
+        return expectedAlpha;
+    }
+
+    private static boolean isNotInRecents(RemoteAnimationTargetCompat app) {
+        return app.isNotInRecents
+                || app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
+    }
+
+    private interface RunningWindowAnim {
+        void end();
+
+        static RunningWindowAnim wrap(Animator animator) {
+            return animator::end;
+        }
+
+        static RunningWindowAnim wrap(RectFSpringAnim rectFSpringAnim) {
+            return rectFSpringAnim::end;
+        }
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
new file mode 100644
index 0000000..c2876180
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -0,0 +1,142 @@
+/*
+ * 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.fallback;
+
+import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.util.FloatProperty;
+import android.view.View;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.LauncherState.ScaleAndTranslation;
+import com.android.launcher3.Utilities;
+import com.android.quickstep.RecentsActivity;
+import com.android.quickstep.util.LayoutUtils;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskView;
+
+public class FallbackRecentsView extends RecentsView<RecentsActivity> {
+
+    public static final FloatProperty<FallbackRecentsView> ZOOM_PROGRESS =
+            new FloatProperty<FallbackRecentsView> ("zoomInProgress") {
+
+                @Override
+                public void setValue(FallbackRecentsView view, float value) {
+                    view.setZoomProgress(value);
+                }
+
+                @Override
+                public Float get(FallbackRecentsView view) {
+                    return view.mZoomInProgress;
+                }
+            };
+
+    private float mZoomInProgress = 0;
+    private boolean mInOverviewState = true;
+
+    private float mZoomScale = 1f;
+    private float mZoomTranslationY = 0f;
+
+    public FallbackRecentsView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public FallbackRecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        setOverviewStateEnabled(true);
+        setOverlayEnabled(true);
+    }
+
+    @Override
+    public void startHome() {
+        mActivity.startHome();
+    }
+
+    @Override
+    public void onViewAdded(View child) {
+        super.onViewAdded(child);
+        updateEmptyMessage();
+    }
+
+    @Override
+    public void onViewRemoved(View child) {
+        super.onViewRemoved(child);
+        updateEmptyMessage();
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        maybeDrawEmptyMessage(canvas);
+        super.draw(canvas);
+    }
+
+    @Override
+    protected void getTaskSize(DeviceProfile dp, Rect outRect) {
+        LayoutUtils.calculateFallbackTaskSize(getContext(), dp, outRect);
+    }
+
+    @Override
+    public boolean shouldUseMultiWindowTaskSizeStrategy() {
+        // Just use the activity task size for multi-window as well.
+        return false;
+    }
+
+    public void resetViewUI() {
+        setZoomProgress(0);
+        resetTaskVisuals();
+    }
+
+    public void setInOverviewState(boolean inOverviewState) {
+        if (mInOverviewState != inOverviewState) {
+            mInOverviewState = inOverviewState;
+            if (mInOverviewState) {
+                resetTaskVisuals();
+            } else {
+                setZoomProgress(1);
+            }
+        }
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+
+        if (getTaskViewCount() == 0) {
+            mZoomScale = 1f;
+            mZoomTranslationY = 0f;
+        } else {
+            TaskView dummyTask = getTaskViewAt(0);
+            ScaleAndTranslation sat = getTempClipAnimationHelper()
+                    .updateForFullscreenOverview(dummyTask)
+                    .getScaleAndTranslation();
+            mZoomScale = sat.scale;
+            mZoomTranslationY = sat.translationY;
+        }
+
+        setZoomProgress(mZoomInProgress);
+    }
+
+    public void setZoomProgress(float progress) {
+        mZoomInProgress = progress;
+        SCALE_PROPERTY.set(this, Utilities.mapRange(mZoomInProgress, 1, mZoomScale));
+        TRANSLATION_Y.set(this, Utilities.mapRange(mZoomInProgress, 0, mZoomTranslationY));
+        FULLSCREEN_PROGRESS.set(this, mZoomInProgress);
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsRootView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsRootView.java
new file mode 100644
index 0000000..1820729
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsRootView.java
@@ -0,0 +1,91 @@
+/*
+ * 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.fallback;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+
+import com.android.launcher3.BaseActivity;
+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;
+
+public class RecentsRootView extends BaseDragLayer<RecentsActivity> {
+
+    private static final int MIN_SIZE = 10;
+    private final RecentsActivity mActivity;
+
+    private final Point mLastKnownSize = new Point(MIN_SIZE, MIN_SIZE);
+
+    public RecentsRootView(Context context, AttributeSet attrs) {
+        super(context, attrs, 1 /* alphaChannelCount */);
+        mActivity = BaseActivity.fromContext(context);
+        setSystemUiVisibility(SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+                | SYSTEM_UI_FLAG_LAYOUT_STABLE);
+    }
+
+    public Point getLastKnownSize() {
+        return mLastKnownSize;
+    }
+
+    public void setup() {
+        mControllers = new TouchController[] { new RecentsTaskController(mActivity) };
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        // Check size changes before the actual measure, to avoid multiple measure calls.
+        int width = Math.max(MIN_SIZE, MeasureSpec.getSize(widthMeasureSpec));
+        int height = Math.max(MIN_SIZE, MeasureSpec.getSize(heightMeasureSpec));
+        if (mLastKnownSize.x != width || mLastKnownSize.y != height) {
+            mLastKnownSize.set(width, height);
+            mActivity.onRootViewSizeChanged();
+        }
+
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+
+    @TargetApi(23)
+    @Override
+    protected boolean fitSystemWindows(Rect insets) {
+        // Update device profile before notifying the children.
+        mActivity.getDeviceProfile().updateInsets(insets);
+        setInsets(insets);
+        return false; // Let children get the full insets
+    }
+
+    @Override
+    public void setInsets(Rect insets) {
+        // If the insets haven't changed, this is a no-op. Avoid unnecessary layout caused by
+        // modifying child layout params.
+        if (!insets.equals(mInsets)) {
+            super.setInsets(insets);
+        }
+        setBackground(insets.top == 0 ? null
+                : Themes.getAttrDrawable(getContext(), R.attr.workspaceStatusBarScrim));
+    }
+
+    public void dispatchInsets() {
+        mActivity.getDeviceProfile().updateInsets(mInsets);
+        super.setInsets(mInsets);
+    }
+}
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsTaskController.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsTaskController.java
new file mode 100644
index 0000000..a113604
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsTaskController.java
@@ -0,0 +1,31 @@
+/*
+ * 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.fallback;
+
+import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
+import com.android.quickstep.RecentsActivity;
+
+public class RecentsTaskController extends TaskViewTouchController<RecentsActivity> {
+
+    public RecentsTaskController(RecentsActivity activity) {
+        super(activity);
+    }
+
+    @Override
+    protected boolean isRecentsInteractive() {
+        return mActivity.hasWindowFocus();
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
new file mode 100644
index 0000000..1f73a28
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.inputconsumers;
+
+import static android.view.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_POINTER_DOWN;
+import static android.view.MotionEvent.ACTION_POINTER_UP;
+import static android.view.MotionEvent.ACTION_UP;
+
+import android.content.Context;
+import android.graphics.RectF;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.Display;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.ViewConfiguration;
+
+import com.android.launcher3.R;
+import com.android.quickstep.util.MotionPauseDetector;
+import com.android.systemui.shared.recents.ISystemUiProxy;
+import com.android.systemui.shared.system.InputMonitorCompat;
+
+/**
+ * Touch consumer for two finger swipe actions for accessibility actions
+ */
+public class AccessibilityInputConsumer extends DelegateInputConsumer {
+
+    private static final String TAG = "A11yInputConsumer";
+
+    private final ISystemUiProxy mSystemUiProxy;
+    private final VelocityTracker mVelocityTracker;
+    private final MotionPauseDetector mMotionPauseDetector;
+    private final boolean mAllowLongClick;
+    private final RectF mSwipeTouchRegion;
+
+    private final float mMinGestureDistance;
+    private final float mMinFlingVelocity;
+
+    private int mActivePointerId = -1;
+    private float mDownY;
+    private float mTotalY;
+
+    public AccessibilityInputConsumer(Context context, ISystemUiProxy systemUiProxy,
+            boolean allowLongClick, InputConsumer delegate, InputMonitorCompat inputMonitor,
+            RectF swipeTouchRegion) {
+        super(delegate, inputMonitor);
+        mSystemUiProxy = systemUiProxy;
+        mVelocityTracker = VelocityTracker.obtain();
+        mMinGestureDistance = context.getResources()
+                .getDimension(R.dimen.accessibility_gesture_min_swipe_distance);
+        mMinFlingVelocity = ViewConfiguration.get(context).getScaledMinimumFlingVelocity();
+        mSwipeTouchRegion = swipeTouchRegion;
+
+        mMotionPauseDetector = new MotionPauseDetector(context);
+        mAllowLongClick = allowLongClick;
+    }
+
+    @Override
+    public int getType() {
+        return TYPE_ACCESSIBILITY | mDelegate.getType();
+    }
+
+    @Override
+    public void onMotionEvent(MotionEvent ev) {
+        if (mState != STATE_DELEGATE_ACTIVE) {
+            mVelocityTracker.addMovement(ev);
+        }
+
+        switch (ev.getActionMasked()) {
+            case ACTION_DOWN: {
+                break;
+            }
+            case ACTION_POINTER_UP: {
+                if (mState == STATE_ACTIVE) {
+                    int pointerIndex = ev.getActionIndex();
+                    int pointerId = ev.getPointerId(pointerIndex);
+                    if (pointerId == mActivePointerId) {
+                        final int newPointerIdx = pointerIndex == 0 ? 1 : 0;
+
+                        mTotalY += (ev.getY(pointerIndex) - mDownY);
+                        mDownY = ev.getY(newPointerIdx);
+                        mActivePointerId = ev.getPointerId(newPointerIdx);
+                    }
+                }
+                break;
+            }
+            case ACTION_POINTER_DOWN: {
+                if (mState == STATE_INACTIVE) {
+                    int pointerIndex = ev.getActionIndex();
+                    if (mSwipeTouchRegion.contains(ev.getX(pointerIndex), ev.getY(pointerIndex))
+                            && mDelegate.allowInterceptByParent()) {
+                        setActive(ev);
+
+                        mActivePointerId = ev.getPointerId(pointerIndex);
+                        mDownY = ev.getY(pointerIndex);
+                    } else {
+                        mState = STATE_DELEGATE_ACTIVE;
+                    }
+                }
+                break;
+            }
+            case ACTION_MOVE: {
+                if (mState == STATE_ACTIVE && mAllowLongClick) {
+                    int pointerIndex = ev.findPointerIndex(mActivePointerId);
+                    if (pointerIndex == -1) {
+                        break;
+                    }
+
+                    mMotionPauseDetector.addPosition(ev.getY(pointerIndex) - mDownY,
+                            ev.getEventTime());
+                }
+                break;
+            }
+            case ACTION_UP:
+                if (mState == STATE_ACTIVE) {
+                    try {
+                        if (mAllowLongClick && mMotionPauseDetector.isPaused()) {
+                            mSystemUiProxy.notifyAccessibilityButtonLongClicked();
+                        } else {
+                            mTotalY += (ev.getY() - mDownY);
+                            mVelocityTracker.computeCurrentVelocity(1000);
+
+                            if ((-mTotalY) > mMinGestureDistance
+                                    || (-mVelocityTracker.getYVelocity()) > mMinFlingVelocity) {
+                                mSystemUiProxy.notifyAccessibilityButtonClicked(
+                                        Display.DEFAULT_DISPLAY);
+                            }
+                        }
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Unable to notify accessibility event", e);
+                    }
+                }
+                // Follow through
+            case ACTION_CANCEL: {
+                mVelocityTracker.recycle();
+                mMotionPauseDetector.clear();
+                break;
+            }
+        }
+
+        if (mState != STATE_ACTIVE) {
+            mDelegate.onMotionEvent(ev);
+        }
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java
new file mode 100644
index 0000000..38b5a13
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.inputconsumers;
+
+import static android.view.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_POINTER_DOWN;
+import static android.view.MotionEvent.ACTION_POINTER_UP;
+import static android.view.MotionEvent.ACTION_UP;
+
+import static com.android.launcher3.Utilities.squaredHypot;
+import static 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.ValueAnimator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.PointF;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.GestureDetector;
+import android.view.GestureDetector.SimpleOnGestureListener;
+import android.view.HapticFeedbackConstants;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+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.ActivityControlHelper;
+import com.android.systemui.shared.recents.ISystemUiProxy;
+import com.android.systemui.shared.system.InputMonitorCompat;
+
+/**
+ * Touch consumer for handling events to launch assistant from launcher
+ */
+public class AssistantTouchConsumer extends DelegateInputConsumer {
+
+    private static final String TAG = "AssistantTouchConsumer";
+    private static final long RETRACT_ANIMATION_DURATION_MS = 300;
+
+    // From //java/com/google/android/apps/gsa/search/shared/util/OpaContract.java.
+    private static final String OPA_BUNDLE_TRIGGER = "triggered_by";
+    // From //java/com/google/android/apps/gsa/assistant/shared/proto/opa_trigger.proto.
+    private static final int OPA_BUNDLE_TRIGGER_DIAG_SWIPE_GESTURE = 83;
+    private static final String INVOCATION_TYPE_KEY = "invocation_type";
+    private static final int INVOCATION_TYPE_GESTURE = 1;
+
+    private final PointF mDownPos = new PointF();
+    private final PointF mLastPos = new PointF();
+    private final PointF mStartDragPos = new PointF();
+
+    private int mActivePointerId = -1;
+    private boolean mPassedSlop;
+    private boolean mLaunchedAssistant;
+    private float mDistance;
+    private float mTimeFraction;
+    private long mDragTime;
+    private float mLastProgress;
+    private int mDirection;
+    private ActivityControlHelper mActivityControlHelper;
+
+    private final float mDistThreshold;
+    private final long mTimeThreshold;
+    private final int mAngleThreshold;
+    private final float mSquaredSlop;
+    private final ISystemUiProxy mSysUiProxy;
+    private final Context mContext;
+    private final GestureDetector mGestureDetector;
+
+    public AssistantTouchConsumer(Context context, ISystemUiProxy systemUiProxy,
+            ActivityControlHelper activityControlHelper, InputConsumer delegate,
+            InputMonitorCompat inputMonitor) {
+        super(delegate, inputMonitor);
+        final Resources res = context.getResources();
+        mContext = context;
+        mSysUiProxy = systemUiProxy;
+        mDistThreshold = res.getDimension(R.dimen.gestures_assistant_drag_threshold);
+        mTimeThreshold = res.getInteger(R.integer.assistant_gesture_min_time_threshold);
+        mAngleThreshold = res.getInteger(R.integer.assistant_gesture_corner_deg_threshold);
+
+        float slop = ViewConfiguration.get(context).getScaledTouchSlop();
+
+        mSquaredSlop = slop * slop;
+        mActivityControlHelper = activityControlHelper;
+
+        mGestureDetector = new GestureDetector(context, new AssistantGestureListener());
+    }
+
+    @Override
+    public int getType() {
+        return TYPE_ASSISTANT | mDelegate.getType();
+    }
+
+    @Override
+    public void onMotionEvent(MotionEvent ev) {
+        // TODO add logging
+        mGestureDetector.onTouchEvent(ev);
+
+        switch (ev.getActionMasked()) {
+            case ACTION_DOWN: {
+                mActivePointerId = ev.getPointerId(0);
+                mDownPos.set(ev.getX(), ev.getY());
+                mLastPos.set(mDownPos);
+                mTimeFraction = 0;
+                break;
+            }
+            case ACTION_POINTER_DOWN: {
+                if (mState != STATE_ACTIVE) {
+                    mState = STATE_DELEGATE_ACTIVE;
+                    break;
+                }
+            }
+            case ACTION_POINTER_UP: {
+                int ptrIdx = ev.getActionIndex();
+                int ptrId = ev.getPointerId(ptrIdx);
+                if (ptrId == mActivePointerId) {
+                    final int newPointerIdx = ptrIdx == 0 ? 1 : 0;
+                    mDownPos.set(
+                        ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x),
+                        ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y));
+                    mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx));
+                    mActivePointerId = ev.getPointerId(newPointerIdx);
+                }
+                break;
+            }
+            case ACTION_MOVE: {
+                if (mState == STATE_DELEGATE_ACTIVE) {
+                    break;
+                }
+                if (!mDelegate.allowInterceptByParent()) {
+                    mState = STATE_DELEGATE_ACTIVE;
+                    break;
+                }
+                int pointerIndex = ev.findPointerIndex(mActivePointerId);
+                if (pointerIndex == -1) {
+                    break;
+                }
+                mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
+
+                if (!mPassedSlop) {
+                    // Normal gesture, ensure we pass the slop before we start tracking the gesture
+                    if (squaredHypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y)
+                            > mSquaredSlop) {
+
+                        mPassedSlop = true;
+                        mStartDragPos.set(mLastPos.x, mLastPos.y);
+                        mDragTime = SystemClock.uptimeMillis();
+
+                        if (isValidAssistantGestureAngle(
+                            mDownPos.x - mLastPos.x, mDownPos.y - mLastPos.y)) {
+                            setActive(ev);
+                        } else {
+                            mState = STATE_DELEGATE_ACTIVE;
+                        }
+                    }
+                } else {
+                    // Movement
+                    mDistance = (float) Math.hypot(mLastPos.x - mStartDragPos.x,
+                        mLastPos.y - mStartDragPos.y);
+                    if (mDistance >= 0) {
+                        final long diff = SystemClock.uptimeMillis() - mDragTime;
+                        mTimeFraction = Math.min(diff * 1f / mTimeThreshold, 1);
+                        updateAssistantProgress();
+                    }
+                }
+                break;
+            }
+            case ACTION_CANCEL:
+            case ACTION_UP:
+                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();
+                        try {
+
+                            mSysUiProxy.onAssistantProgress(progress);
+                        } catch (RemoteException e) {
+                            Log.w(TAG, "Failed to send SysUI start/send assistant progress: "
+                                + progress, e);
+                        }
+                    });
+                    animator.setInterpolator(Interpolators.DEACCEL_2);
+                    animator.start();
+                }
+                mPassedSlop = false;
+                mState = STATE_INACTIVE;
+                break;
+        }
+
+        if (mState != STATE_ACTIVE) {
+            mDelegate.onMotionEvent(ev);
+        }
+    }
+
+    private void updateAssistantProgress() {
+        if (!mLaunchedAssistant) {
+            mLastProgress = Math.min(mDistance * 1f / mDistThreshold, 1) * mTimeFraction;
+            try {
+                if (mDistance >= mDistThreshold && mTimeFraction >= 1) {
+                    mSysUiProxy.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);
+                    mSysUiProxy.startAssistant(args);
+                    mLaunchedAssistant = true;
+                } else {
+                    mSysUiProxy.onAssistantProgress(mLastProgress);
+                }
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed to send SysUI start/send assistant progress: " + mLastProgress,
+                    e);
+            }
+        }
+    }
+
+    private void startAssistantInternal(int gestureType) {
+        UserEventDispatcher.newInstance(mContext)
+            .logActionOnContainer(gestureType, mDirection, NAVBAR);
+
+        BaseDraggingActivity launcherActivity = mActivityControlHelper
+            .getCreatedActivity();
+        if (launcherActivity != null) {
+            launcherActivity.getRootView().performHapticFeedback(
+                13, // HapticFeedbackConstants.GESTURE_END
+                HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+        }
+    }
+
+    /**
+     * Determine if angle is larger than threshold for assistant detection
+     */
+    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
+        angle = angle > 90 ? 180 - angle : angle;
+        return (angle > mAngleThreshold && angle < 90);
+    }
+
+    private class AssistantGestureListener extends SimpleOnGestureListener {
+        @Override
+        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
+            if (isValidAssistantGestureAngle(velocityX, -velocityY)
+                && !mLaunchedAssistant && mState != STATE_DELEGATE_ACTIVE) {
+                mLastProgress = 1;
+                try {
+                    mSysUiProxy.onAssistantGestureCompletion(
+                        (float) Math.sqrt(velocityX * velocityX + velocityY * velocityY));
+                    startAssistantInternal(FLING);
+
+                    Bundle args = new Bundle();
+                    args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_GESTURE);
+                    mSysUiProxy.startAssistant(args);
+                    mLaunchedAssistant = true;
+                } catch (RemoteException e) {
+                    Log.w(TAG,
+                        "Failed to send SysUI start/send assistant progress: " + mLastProgress,
+                        e);
+                }
+            }
+            return true;
+        }
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
new file mode 100644
index 0000000..311ddd2
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
@@ -0,0 +1,49 @@
+package com.android.quickstep.inputconsumers;
+
+import android.view.MotionEvent;
+
+import com.android.systemui.shared.system.InputMonitorCompat;
+
+public abstract class DelegateInputConsumer implements InputConsumer {
+
+    protected static final int STATE_INACTIVE = 0;
+    protected static final int STATE_ACTIVE = 1;
+    protected static final int STATE_DELEGATE_ACTIVE = 2;
+
+    protected final InputConsumer mDelegate;
+    protected final InputMonitorCompat mInputMonitor;
+
+    protected int mState;
+
+    public DelegateInputConsumer(InputConsumer delegate, InputMonitorCompat inputMonitor) {
+        mDelegate = delegate;
+        mInputMonitor = inputMonitor;
+        mState = STATE_INACTIVE;
+    }
+
+    @Override
+    public boolean useSharedSwipeState() {
+        return mDelegate.useSharedSwipeState();
+    }
+
+    @Override
+    public boolean allowInterceptByParent() {
+        return mDelegate.allowInterceptByParent() && mState != STATE_ACTIVE;
+    }
+
+    @Override
+    public void onConsumerAboutToBeSwitched() {
+        mDelegate.onConsumerAboutToBeSwitched();
+    }
+
+    protected void setActive(MotionEvent ev) {
+        mState = STATE_ACTIVE;
+        mInputMonitor.pilferPointers();
+
+        // Send cancel event
+        MotionEvent event = MotionEvent.obtain(ev);
+        event.setAction(MotionEvent.ACTION_CANCEL);
+        mDelegate.onMotionEvent(event);
+        event.recycle();
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
new file mode 100644
index 0000000..db2af59
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -0,0 +1,256 @@
+/*
+ * 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_POINTER_DOWN;
+import static android.view.MotionEvent.ACTION_UP;
+
+import static com.android.launcher3.Utilities.squaredHypot;
+import static com.android.launcher3.Utilities.squaredTouchSlop;
+import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
+import static com.android.quickstep.WindowTransformSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.ViewConfiguration;
+import android.view.WindowManager;
+
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.quickstep.LockScreenRecentsActivity;
+import com.android.quickstep.MultiStateCallback;
+import com.android.quickstep.SwipeSharedState;
+import com.android.quickstep.util.ClipAnimationHelper;
+import com.android.quickstep.util.RecentsAnimationListenerSet;
+import com.android.quickstep.util.SwipeAnimationTargetSet;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.BackgroundExecutor;
+import com.android.systemui.shared.system.InputMonitorCompat;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+/**
+ * A dummy input consumer used when the device is still locked, e.g. from secure camera.
+ */
+public class DeviceLockedInputConsumer implements InputConsumer,
+        SwipeAnimationTargetSet.SwipeAnimationListener {
+
+    private static final float SCALE_DOWN = 0.75f;
+
+    private static final String[] STATE_NAMES = DEBUG_STATES ? new String[2] : null;
+    private static int getFlagForIndex(int index, String name) {
+        if (DEBUG_STATES) {
+            STATE_NAMES[index] = name;
+        }
+        return 1 << index;
+    }
+
+    private static final int STATE_TARGET_RECEIVED =
+            getFlagForIndex(0, "STATE_TARGET_RECEIVED");
+    private static final int STATE_HANDLER_INVALIDATED =
+            getFlagForIndex(1, "STATE_HANDLER_INVALIDATED");
+
+    private final Context mContext;
+    private final float mTouchSlopSquared;
+    private final SwipeSharedState mSwipeSharedState;
+    private final InputMonitorCompat mInputMonitorCompat;
+
+    private final PointF mTouchDown = new PointF();
+    private final ClipAnimationHelper mClipAnimationHelper;
+    private final ClipAnimationHelper.TransformParams mTransformParams;
+    private final Point mDisplaySize;
+    private final MultiStateCallback mStateCallback;
+    private final RectF mSwipeTouchRegion;
+    public final int mRunningTaskId;
+
+    private VelocityTracker mVelocityTracker;
+    private float mProgress;
+
+    private boolean mThresholdCrossed = false;
+
+    private SwipeAnimationTargetSet mTargetSet;
+
+    public DeviceLockedInputConsumer(Context context, SwipeSharedState swipeSharedState,
+            InputMonitorCompat inputMonitorCompat, RectF swipeTouchRegion, int runningTaskId) {
+        mContext = context;
+        mTouchSlopSquared = squaredTouchSlop(context);
+        mSwipeSharedState = swipeSharedState;
+        mClipAnimationHelper = new ClipAnimationHelper(context);
+        mTransformParams = new ClipAnimationHelper.TransformParams();
+        mInputMonitorCompat = inputMonitorCompat;
+        mSwipeTouchRegion = swipeTouchRegion;
+        mRunningTaskId = runningTaskId;
+
+        // Do not use DeviceProfile as the user data might be locked
+        mDisplaySize = new Point();
+        context.getSystemService(WindowManager.class).getDefaultDisplay().getRealSize(mDisplaySize);
+
+        // Init states
+        mStateCallback = new MultiStateCallback(STATE_NAMES);
+        mStateCallback.addCallback(STATE_TARGET_RECEIVED | STATE_HANDLER_INVALIDATED,
+                this::endRemoteAnimation);
+
+        mVelocityTracker = VelocityTracker.obtain();
+    }
+
+    @Override
+    public int getType() {
+        return TYPE_DEVICE_LOCKED;
+    }
+
+    @Override
+    public void onMotionEvent(MotionEvent ev) {
+        if (mVelocityTracker == null) {
+            return;
+        }
+        mVelocityTracker.addMovement(ev);
+
+        float x = ev.getX();
+        float y = ev.getY();
+        switch (ev.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+                mTouchDown.set(x, y);
+                break;
+            case ACTION_POINTER_DOWN: {
+                if (!mThresholdCrossed) {
+                    // Cancel interaction in case of multi-touch interaction
+                    int ptrIdx = ev.getActionIndex();
+                    if (!mSwipeTouchRegion.contains(ev.getX(ptrIdx), ev.getY(ptrIdx))) {
+                        int action = ev.getAction();
+                        ev.setAction(ACTION_CANCEL);
+                        finishTouchTracking(ev);
+                        ev.setAction(action);
+                    }
+                }
+                break;
+            }
+            case MotionEvent.ACTION_MOVE: {
+                if (!mThresholdCrossed) {
+                    if (squaredHypot(x - mTouchDown.x, y - mTouchDown.y) > mTouchSlopSquared) {
+                        startRecentsTransition();
+                    }
+                } else {
+                    float dy = Math.max(mTouchDown.y - y, 0);
+                    mProgress = dy / mDisplaySize.y;
+                    mTransformParams.setProgress(mProgress);
+                    if (mTargetSet != null) {
+                        mClipAnimationHelper.applyTransform(mTargetSet, mTransformParams);
+                    }
+                }
+                break;
+            }
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_UP:
+                finishTouchTracking(ev);
+                break;
+        }
+    }
+
+    /**
+     * Called when the gesture has ended. Does not correlate to the completion of the interaction as
+     * the animation can still be running.
+     */
+    private void finishTouchTracking(MotionEvent ev) {
+        mStateCallback.setState(STATE_HANDLER_INVALIDATED);
+        if (mThresholdCrossed && ev.getAction() == ACTION_UP) {
+            mVelocityTracker.computeCurrentVelocity(1000,
+                    ViewConfiguration.get(mContext).getScaledMaximumFlingVelocity());
+
+            float velocityY = mVelocityTracker.getYVelocity();
+            float flingThreshold = mContext.getResources()
+                    .getDimension(R.dimen.quickstep_fling_threshold_velocity);
+
+            boolean dismissTask;
+            if (Math.abs(velocityY) > flingThreshold) {
+                // Is fling
+                dismissTask = velocityY < 0;
+            } else {
+                dismissTask = mProgress >= (1 - MIN_PROGRESS_FOR_OVERVIEW);
+            }
+            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));
+            }
+        }
+        mVelocityTracker.recycle();
+        mVelocityTracker = null;
+    }
+
+    private void startRecentsTransition() {
+        mThresholdCrossed = true;
+        RecentsAnimationListenerSet newListenerSet =
+                mSwipeSharedState.newRecentsAnimationListenerSet();
+        newListenerSet.addListener(this);
+        Intent intent = new Intent(Intent.ACTION_MAIN)
+                .addCategory(Intent.CATEGORY_DEFAULT)
+                .setComponent(new ComponentName(mContext, LockScreenRecentsActivity.class))
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+
+        mInputMonitorCompat.pilferPointers();
+        BackgroundExecutor.get().submit(
+                () -> ActivityManagerWrapper.getInstance().startRecentsActivity(
+                        intent, null, newListenerSet, null, null));
+    }
+
+    @Override
+    public void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet) {
+        mTargetSet = targetSet;
+
+        Rect displaySize = new Rect(0, 0, mDisplaySize.x, mDisplaySize.y);
+        RemoteAnimationTargetCompat targetCompat = targetSet.findTask(mRunningTaskId);
+        if (targetCompat != null) {
+            mClipAnimationHelper.updateSource(displaySize, targetCompat);
+        }
+
+        Utilities.scaleRectAboutCenter(displaySize, SCALE_DOWN);
+        displaySize.offsetTo(displaySize.left, 0);
+        mClipAnimationHelper.updateTargetRect(displaySize);
+        mClipAnimationHelper.applyTransform(mTargetSet, mTransformParams);
+
+        mStateCallback.setState(STATE_TARGET_RECEIVED);
+    }
+
+    @Override
+    public void onRecentsAnimationCanceled() {
+        mTargetSet = null;
+    }
+
+    private void endRemoteAnimation() {
+        if (mTargetSet != null) {
+            mTargetSet.finishController(
+                    false /* toRecents */, null /* callback */, false /* sendUserLeaveHint */);
+        }
+    }
+
+    @Override
+    public void onConsumerAboutToBeSwitched() {
+        mStateCallback.setState(STATE_HANDLER_INVALIDATED);
+    }
+
+    @Override
+    public boolean allowInterceptByParent() {
+        return !mThresholdCrossed;
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java
new file mode 100644
index 0000000..d05ca2a
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.inputconsumers;
+
+import static android.view.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_POINTER_DOWN;
+import static android.view.MotionEvent.ACTION_POINTER_UP;
+import static android.view.MotionEvent.ACTION_UP;
+import static android.view.MotionEvent.INVALID_POINTER_ID;
+
+import static com.android.quickstep.WindowTransformSwipeHandler.MAX_SWIPE_DURATION;
+import static com.android.quickstep.WindowTransformSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW;
+import static com.android.quickstep.WindowTransformSwipeHandler.MIN_SWIPE_DURATION;
+import static com.android.quickstep.inputconsumers.OtherActivityInputConsumer.QUICKSTEP_TOUCH_SLOP_RATIO;
+import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.ViewConfiguration;
+import android.view.WindowManager;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.R;
+import com.android.quickstep.ActivityControlHelper;
+import com.android.quickstep.OverviewComponentObserver;
+import com.android.quickstep.SwipeSharedState;
+import com.android.quickstep.util.ClipAnimationHelper;
+import com.android.quickstep.util.ClipAnimationHelper.TransformParams;
+import com.android.quickstep.util.NavBarPosition;
+import com.android.quickstep.util.RecentsAnimationListenerSet;
+import com.android.quickstep.util.SwipeAnimationTargetSet;
+import com.android.quickstep.util.SwipeAnimationTargetSet.SwipeAnimationListener;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.BackgroundExecutor;
+import com.android.systemui.shared.system.InputMonitorCompat;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+public class FallbackNoButtonInputConsumer implements InputConsumer, SwipeAnimationListener {
+
+    private static final int STATE_NOT_FINISHED = 0;
+    private static final int STATE_FINISHED_TO_HOME = 1;
+    private static final int STATE_FINISHED_TO_APP = 2;
+
+    private static final float PROGRESS_TO_END_GESTURE = -2;
+
+    private final ActivityControlHelper mActivityControlHelper;
+    private final InputMonitorCompat mInputMonitor;
+    private final Context mContext;
+    private final NavBarPosition mNavBarPosition;
+    private final SwipeSharedState mSwipeSharedState;
+    private final OverviewComponentObserver mOverviewComponentObserver;
+    private final int mRunningTaskId;
+
+    private final ClipAnimationHelper mClipAnimationHelper;
+    private final TransformParams mTransformParams = new TransformParams();
+    private final float mTransitionDragLength;
+    private final DeviceProfile mDP;
+
+    private final RectF mSwipeTouchRegion;
+    private final boolean mDisableHorizontalSwipe;
+
+    private final PointF mDownPos = new PointF();
+    private final PointF mLastPos = new PointF();
+
+    private int mActivePointerId = -1;
+    // Slop used to determine when we say that the gesture has started.
+    private boolean mPassedPilferInputSlop;
+
+    private VelocityTracker mVelocityTracker;
+
+    // Distance after which we start dragging the window.
+    private final float mTouchSlop;
+
+    // Might be displacement in X or Y, depending on the direction we are swiping from the nav bar.
+    private float mStartDisplacement;
+    private SwipeAnimationTargetSet mSwipeAnimationTargetSet;
+    private float mProgress;
+
+    private int mState = STATE_NOT_FINISHED;
+
+    public FallbackNoButtonInputConsumer(Context context,
+            ActivityControlHelper activityControlHelper, InputMonitorCompat inputMonitor,
+            SwipeSharedState swipeSharedState, RectF swipeTouchRegion,
+            OverviewComponentObserver overviewComponentObserver,
+            boolean disableHorizontalSwipe, RunningTaskInfo runningTaskInfo) {
+        mContext = context;
+        mActivityControlHelper = activityControlHelper;
+        mInputMonitor = inputMonitor;
+        mOverviewComponentObserver = overviewComponentObserver;
+        mRunningTaskId = runningTaskInfo.id;
+
+        mSwipeSharedState = swipeSharedState;
+        mSwipeTouchRegion = swipeTouchRegion;
+        mDisableHorizontalSwipe = disableHorizontalSwipe;
+
+        mNavBarPosition = new NavBarPosition(context);
+        mVelocityTracker = VelocityTracker.obtain();
+
+        mTouchSlop = QUICKSTEP_TOUCH_SLOP_RATIO
+                * ViewConfiguration.get(context).getScaledTouchSlop();
+
+        mClipAnimationHelper = new ClipAnimationHelper(context);
+
+        mDP = InvariantDeviceProfile.INSTANCE.get(context).getDeviceProfile(context).copy(context);
+        Rect tempRect = new Rect();
+        mTransitionDragLength = mActivityControlHelper.getSwipeUpDestinationAndLength(
+                mDP, context, tempRect);
+        mClipAnimationHelper.updateTargetRect(tempRect);
+    }
+
+    @Override
+    public int getType() {
+        return TYPE_FALLBACK_NO_BUTTON;
+    }
+
+    @Override
+    public void onMotionEvent(MotionEvent ev) {
+        if (mVelocityTracker == null) {
+            return;
+        }
+
+        mVelocityTracker.addMovement(ev);
+        if (ev.getActionMasked() == ACTION_POINTER_UP) {
+            mVelocityTracker.clear();
+        }
+
+        switch (ev.getActionMasked()) {
+            case ACTION_DOWN: {
+                mActivePointerId = ev.getPointerId(0);
+                mDownPos.set(ev.getX(), ev.getY());
+                mLastPos.set(mDownPos);
+                break;
+            }
+            case ACTION_POINTER_DOWN: {
+                if (!mPassedPilferInputSlop) {
+                    // Cancel interaction in case of multi-touch interaction
+                    int ptrIdx = ev.getActionIndex();
+                    if (!mSwipeTouchRegion.contains(ev.getX(ptrIdx), ev.getY(ptrIdx))) {
+                        forceCancelGesture(ev);
+                    }
+                }
+                break;
+            }
+            case ACTION_POINTER_UP: {
+                int ptrIdx = ev.getActionIndex();
+                int ptrId = ev.getPointerId(ptrIdx);
+                if (ptrId == mActivePointerId) {
+                    final int newPointerIdx = ptrIdx == 0 ? 1 : 0;
+                    mDownPos.set(
+                            ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x),
+                            ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y));
+                    mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx));
+                    mActivePointerId = ev.getPointerId(newPointerIdx);
+                }
+                break;
+            }
+            case ACTION_MOVE: {
+                int pointerIndex = ev.findPointerIndex(mActivePointerId);
+                if (pointerIndex == INVALID_POINTER_ID) {
+                    break;
+                }
+                mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
+                float displacement = getDisplacement(ev);
+
+                if (!mPassedPilferInputSlop) {
+                    if (mDisableHorizontalSwipe && Math.abs(mLastPos.x - mDownPos.x)
+                            > Math.abs(mLastPos.y - mDownPos.y)) {
+                        // Horizontal gesture is not allowed in this region
+                        forceCancelGesture(ev);
+                        break;
+                    }
+
+                    if (Math.abs(displacement) >= mTouchSlop) {
+                        mPassedPilferInputSlop = true;
+
+                        // Deferred gesture, start the animation and gesture tracking once
+                        // we pass the actual touch slop
+                        startTouchTrackingForWindowAnimation(displacement);
+                    }
+                } else {
+                    updateDisplacement(displacement - mStartDisplacement);
+                }
+                break;
+            }
+            case ACTION_CANCEL:
+            case ACTION_UP: {
+                finishTouchTracking(ev);
+                break;
+            }
+        }
+    }
+
+    private void startTouchTrackingForWindowAnimation(float displacement) {
+        mStartDisplacement = Math.min(displacement, -mTouchSlop);
+
+        RecentsAnimationListenerSet listenerSet =
+                mSwipeSharedState.newRecentsAnimationListenerSet();
+        listenerSet.addListener(this);
+        Intent homeIntent = mOverviewComponentObserver.getHomeIntent();
+        BackgroundExecutor.get().submit(
+                () -> ActivityManagerWrapper.getInstance().startRecentsActivity(
+                        homeIntent, null, listenerSet, null, null));
+
+        ActivityManagerWrapper.getInstance().closeSystemWindows(
+                CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+        mInputMonitor.pilferPointers();
+    }
+
+    private void updateDisplacement(float displacement) {
+        mProgress = displacement / mTransitionDragLength;
+        mTransformParams.setProgress(mProgress);
+
+        if (mSwipeAnimationTargetSet != null) {
+            mClipAnimationHelper.applyTransform(mSwipeAnimationTargetSet, mTransformParams);
+        }
+    }
+
+    private void forceCancelGesture(MotionEvent ev) {
+        int action = ev.getAction();
+        ev.setAction(ACTION_CANCEL);
+        finishTouchTracking(ev);
+        ev.setAction(action);
+    }
+
+    /**
+     * Called when the gesture has ended. Does not correlate to the completion of the interaction as
+     * the animation can still be running.
+     */
+    private void finishTouchTracking(MotionEvent ev) {
+        if (ev.getAction() == ACTION_CANCEL) {
+            mState = STATE_FINISHED_TO_APP;
+        } else {
+            mVelocityTracker.computeCurrentVelocity(1000,
+                    ViewConfiguration.get(mContext).getScaledMaximumFlingVelocity());
+            float velocityX = mVelocityTracker.getXVelocity(mActivePointerId);
+            float velocityY = mVelocityTracker.getYVelocity(mActivePointerId);
+            float velocity = mNavBarPosition.isRightEdge() ? velocityX
+                    : mNavBarPosition.isLeftEdge() ? -velocityX
+                            : velocityY;
+            float flingThreshold = mContext.getResources()
+                    .getDimension(R.dimen.quickstep_fling_threshold_velocity);
+            boolean isFling = Math.abs(velocity) > flingThreshold;
+
+            boolean goingHome;
+            if (!isFling) {
+                goingHome = -mProgress >= MIN_PROGRESS_FOR_OVERVIEW;
+            } else {
+                goingHome = velocity < 0;
+            }
+
+            if (goingHome) {
+                mState = STATE_FINISHED_TO_HOME;
+            } else {
+                mState = STATE_FINISHED_TO_APP;
+            }
+        }
+
+        if (mSwipeAnimationTargetSet != null) {
+            finishAnimationTargetSet();
+        }
+    }
+
+    private void finishAnimationTargetSet() {
+        if (mState == STATE_FINISHED_TO_APP) {
+            mSwipeAnimationTargetSet.finishController(false, null, false);
+        } else {
+            if (mProgress < PROGRESS_TO_END_GESTURE) {
+                mSwipeAnimationTargetSet.finishController(true, null, true);
+            } else {
+                long duration = (long) (Math.min(mProgress - PROGRESS_TO_END_GESTURE, 1)
+                        * MAX_SWIPE_DURATION / Math.abs(PROGRESS_TO_END_GESTURE));
+                if (duration < 0) {
+                    duration = MIN_SWIPE_DURATION;
+                }
+
+                ValueAnimator anim = ValueAnimator.ofFloat(mProgress, PROGRESS_TO_END_GESTURE);
+                anim.addUpdateListener(a -> {
+                    float p = (Float) anim.getAnimatedValue();
+                    mTransformParams.setProgress(p);
+                    mClipAnimationHelper.applyTransform(mSwipeAnimationTargetSet, mTransformParams);
+                });
+                anim.setDuration(duration);
+                anim.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        mSwipeAnimationTargetSet.finishController(true, null, true);
+                    }
+                });
+                anim.start();
+            }
+        }
+    }
+
+    @Override
+    public void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet) {
+        mSwipeAnimationTargetSet = targetSet;
+        Rect overviewStackBounds = new Rect(0, 0, mDP.widthPx, mDP.heightPx);
+        RemoteAnimationTargetCompat runningTaskTarget = targetSet.findTask(mRunningTaskId);
+
+        mDP.updateIsSeascape(mContext.getSystemService(WindowManager.class));
+        if (runningTaskTarget != null) {
+            mClipAnimationHelper.updateSource(overviewStackBounds, runningTaskTarget);
+        }
+        mClipAnimationHelper.prepareAnimation(mDP, false /* isOpening */);
+
+        overviewStackBounds
+                .inset(-overviewStackBounds.width() / 5, -overviewStackBounds.height() / 5);
+        mClipAnimationHelper.updateTargetRect(overviewStackBounds);
+        mClipAnimationHelper.applyTransform(mSwipeAnimationTargetSet, mTransformParams);
+
+        if (mState != STATE_NOT_FINISHED) {
+            finishAnimationTargetSet();
+        }
+    }
+
+    @Override
+    public void onRecentsAnimationCanceled() { }
+
+    private float getDisplacement(MotionEvent ev) {
+        if (mNavBarPosition.isRightEdge()) {
+            return ev.getX() - mDownPos.x;
+        } else if (mNavBarPosition.isLeftEdge()) {
+            return mDownPos.x - ev.getX();
+        } else {
+            return ev.getY() - mDownPos.y;
+        }
+    }
+
+    @Override
+    public boolean allowInterceptByParent() {
+        return !mPassedPilferInputSlop;
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java
new file mode 100644
index 0000000..f5cf654
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java
@@ -0,0 +1,95 @@
+/*
+ * 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.inputconsumers;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.view.InputEvent;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+@TargetApi(Build.VERSION_CODES.O)
+public interface InputConsumer {
+
+    int TYPE_NO_OP = 1 << 0;
+    int TYPE_OVERVIEW = 1 << 1;
+    int TYPE_OTHER_ACTIVITY = 1 << 2;
+    int TYPE_ASSISTANT = 1 << 3;
+    int TYPE_DEVICE_LOCKED = 1 << 4;
+    int TYPE_ACCESSIBILITY = 1 << 5;
+    int TYPE_SCREEN_PINNED = 1 << 6;
+    int TYPE_OVERVIEW_WITHOUT_FOCUS = 1 << 7;
+    int TYPE_RESET_GESTURE = 1 << 8;
+    int TYPE_FALLBACK_NO_BUTTON = 1 << 9;
+
+    String[] NAMES = new String[] {
+           "TYPE_NO_OP",                    // 0
+            "TYPE_OVERVIEW",                // 1
+            "TYPE_OTHER_ACTIVITY",          // 2
+            "TYPE_ASSISTANT",               // 3
+            "TYPE_DEVICE_LOCKED",           // 4
+            "TYPE_ACCESSIBILITY",           // 5
+            "TYPE_SCREEN_PINNED",           // 6
+            "TYPE_OVERVIEW_WITHOUT_FOCUS",  // 7
+            "TYPE_RESET_GESTURE",           // 8
+            "TYPE_FALLBACK_NO_BUTTON",      // 9
+    };
+
+    InputConsumer NO_OP = () -> TYPE_NO_OP;
+
+    int getType();
+
+    default boolean useSharedSwipeState() {
+        return false;
+    }
+
+    /**
+     * Returns true if the user has crossed the threshold for it to be an explicit action.
+     */
+    default boolean allowInterceptByParent() {
+        return true;
+    }
+
+    /**
+     * Called by the event queue when the consumer is about to be switched to a new consumer.
+     */
+    default void onConsumerAboutToBeSwitched() { }
+
+    default void onMotionEvent(MotionEvent ev) { }
+
+    default void onKeyEvent(KeyEvent ev) { }
+
+    default void onInputEvent(InputEvent ev) {
+        if (ev instanceof MotionEvent) {
+            onMotionEvent((MotionEvent) ev);
+        } else {
+            onKeyEvent((KeyEvent) ev);
+        }
+    }
+
+    default String getName() {
+        String name = "";
+        for (int i = 0; i < NAMES.length; i++) {
+            if ((getType() & (1 << i)) != 0) {
+                if (name.length() > 0) {
+                    name += ":";
+                }
+                name += NAMES[i];
+            }
+        }
+        return name;
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
new file mode 100644
index 0000000..4c137d3
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -0,0 +1,457 @@
+/*
+ * 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.inputconsumers;
+
+import static android.view.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_POINTER_DOWN;
+import static android.view.MotionEvent.ACTION_POINTER_UP;
+import static android.view.MotionEvent.ACTION_UP;
+import static android.view.MotionEvent.INVALID_POINTER_ID;
+
+import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
+import static com.android.launcher3.Utilities.squaredHypot;
+import static com.android.launcher3.util.RaceConditionTracker.ENTER;
+import static com.android.launcher3.util.RaceConditionTracker.EXIT;
+import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG;
+import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
+
+import android.annotation.TargetApi;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.graphics.PointF;
+import android.graphics.RectF;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.ViewConfiguration;
+
+import com.android.launcher3.R;
+import com.android.launcher3.util.Preconditions;
+import com.android.launcher3.util.RaceConditionTracker;
+import com.android.launcher3.util.TraceHelper;
+import com.android.quickstep.ActivityControlHelper;
+import com.android.quickstep.OverviewCallbacks;
+import com.android.quickstep.RecentsModel;
+import com.android.quickstep.SwipeSharedState;
+import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.SysUINavigationMode.Mode;
+import com.android.quickstep.WindowTransformSwipeHandler;
+import com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget;
+import com.android.quickstep.util.CachedEventDispatcher;
+import com.android.quickstep.util.MotionPauseDetector;
+import com.android.quickstep.util.NavBarPosition;
+import com.android.quickstep.util.RecentsAnimationListenerSet;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.BackgroundExecutor;
+import com.android.systemui.shared.system.InputConsumerController;
+import com.android.systemui.shared.system.InputMonitorCompat;
+
+import java.util.function.Consumer;
+
+import androidx.annotation.UiThread;
+
+/**
+ * Input consumer for handling events originating from an activity other than Launcher
+ */
+@TargetApi(Build.VERSION_CODES.P)
+public class OtherActivityInputConsumer extends ContextWrapper implements InputConsumer {
+
+    public static final String DOWN_EVT = "OtherActivityInputConsumer.DOWN";
+    private static final String UP_EVT = "OtherActivityInputConsumer.UP";
+
+    // TODO: Move to quickstep contract
+    public static final float QUICKSTEP_TOUCH_SLOP_RATIO = 3;
+
+    private final CachedEventDispatcher mRecentsViewDispatcher = new CachedEventDispatcher();
+    private final RunningTaskInfo mRunningTask;
+    private final RecentsModel mRecentsModel;
+    private final Intent mHomeIntent;
+    private final ActivityControlHelper mActivityControlHelper;
+    private final OverviewCallbacks mOverviewCallbacks;
+    private final InputConsumerController mInputConsumer;
+    private final SwipeSharedState mSwipeSharedState;
+    private final InputMonitorCompat mInputMonitorCompat;
+    private final SysUINavigationMode.Mode mMode;
+    private final RectF mSwipeTouchRegion;
+
+    private final NavBarPosition mNavBarPosition;
+
+    private final Consumer<OtherActivityInputConsumer> mOnCompleteCallback;
+    private final MotionPauseDetector mMotionPauseDetector;
+    private final float mMotionPauseMinDisplacement;
+    private VelocityTracker mVelocityTracker;
+
+    private WindowTransformSwipeHandler mInteractionHandler;
+
+    private final boolean mIsDeferredDownTarget;
+    private final PointF mDownPos = new PointF();
+    private final PointF mLastPos = new PointF();
+    private int mActivePointerId = INVALID_POINTER_ID;
+
+    // Distance after which we start dragging the window.
+    private final float mTouchSlop;
+
+    private final float mSquaredTouchSlop;
+    private final boolean mDisableHorizontalSwipe;
+
+    // Slop used to check when we start moving window.
+    private boolean mPaddedWindowMoveSlop;
+    // Slop used to determine when we say that the gesture has started.
+    private boolean mPassedPilferInputSlop;
+
+    // Might be displacement in X or Y, depending on the direction we are swiping from the nav bar.
+    private float mStartDisplacement;
+
+    private Handler mMainThreadHandler;
+    private Runnable mCancelRecentsAnimationRunnable = () -> {
+        ActivityManagerWrapper.getInstance().cancelRecentsAnimation(
+                true /* restoreHomeStackPosition */);
+    };
+
+    public OtherActivityInputConsumer(Context base, RunningTaskInfo runningTaskInfo,
+            RecentsModel recentsModel, Intent homeIntent, ActivityControlHelper activityControl,
+            boolean isDeferredDownTarget, OverviewCallbacks overviewCallbacks,
+            InputConsumerController inputConsumer,
+            Consumer<OtherActivityInputConsumer> onCompleteCallback,
+            SwipeSharedState swipeSharedState, InputMonitorCompat inputMonitorCompat,
+            RectF swipeTouchRegion, boolean disableHorizontalSwipe) {
+        super(base);
+
+        mMainThreadHandler = new Handler(Looper.getMainLooper());
+        mRunningTask = runningTaskInfo;
+        mRecentsModel = recentsModel;
+        mHomeIntent = homeIntent;
+        mMode = SysUINavigationMode.getMode(base);
+        mSwipeTouchRegion = swipeTouchRegion;
+
+        mMotionPauseDetector = new MotionPauseDetector(base);
+        mMotionPauseMinDisplacement = base.getResources().getDimension(
+                R.dimen.motion_pause_detector_min_displacement_from_app);
+        mOnCompleteCallback = onCompleteCallback;
+        mVelocityTracker = VelocityTracker.obtain();
+        mInputMonitorCompat = inputMonitorCompat;
+
+        mActivityControlHelper = activityControl;
+        boolean continuingPreviousGesture = swipeSharedState.getActiveListener() != null;
+        mIsDeferredDownTarget = !continuingPreviousGesture && isDeferredDownTarget;
+        mOverviewCallbacks = overviewCallbacks;
+        mInputConsumer = inputConsumer;
+        mSwipeSharedState = swipeSharedState;
+
+        mNavBarPosition = new NavBarPosition(base);
+        mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
+
+        float slop = QUICKSTEP_TOUCH_SLOP_RATIO * mTouchSlop;
+        mSquaredTouchSlop = slop * slop;
+
+        mPassedPilferInputSlop = mPaddedWindowMoveSlop = continuingPreviousGesture;
+        mDisableHorizontalSwipe = !mPassedPilferInputSlop && disableHorizontalSwipe;
+    }
+
+    @Override
+    public int getType() {
+        return TYPE_OTHER_ACTIVITY;
+    }
+
+    private void forceCancelGesture(MotionEvent ev) {
+        int action = ev.getAction();
+        ev.setAction(ACTION_CANCEL);
+        finishTouchTracking(ev);
+        ev.setAction(action);
+    }
+
+    @Override
+    public void onMotionEvent(MotionEvent ev) {
+        if (mVelocityTracker == null) {
+            return;
+        }
+
+        // Proxy events to recents view
+        if (mPaddedWindowMoveSlop && mInteractionHandler != null
+                && !mRecentsViewDispatcher.hasConsumer()) {
+            mRecentsViewDispatcher.setConsumer(mInteractionHandler.getRecentsViewDispatcher(
+                    mNavBarPosition.getRotationMode()));
+        }
+        int edgeFlags = ev.getEdgeFlags();
+        ev.setEdgeFlags(edgeFlags | EDGE_NAV_BAR);
+        mRecentsViewDispatcher.dispatchEvent(ev);
+        ev.setEdgeFlags(edgeFlags);
+
+        mVelocityTracker.addMovement(ev);
+        if (ev.getActionMasked() == ACTION_POINTER_UP) {
+            mVelocityTracker.clear();
+            mMotionPauseDetector.clear();
+        }
+
+        switch (ev.getActionMasked()) {
+            case ACTION_DOWN: {
+                RaceConditionTracker.onEvent(DOWN_EVT, ENTER);
+                TraceHelper.beginSection("TouchInt");
+                mActivePointerId = ev.getPointerId(0);
+                mDownPos.set(ev.getX(), ev.getY());
+                mLastPos.set(mDownPos);
+
+                // Start the window animation on down to give more time for launcher to draw if the
+                // user didn't start the gesture over the back button
+                if (!mIsDeferredDownTarget) {
+                    startTouchTrackingForWindowAnimation(ev.getEventTime());
+                }
+
+                RaceConditionTracker.onEvent(DOWN_EVT, EXIT);
+                break;
+            }
+            case ACTION_POINTER_DOWN: {
+                if (!mPassedPilferInputSlop) {
+                    // Cancel interaction in case of multi-touch interaction
+                    int ptrIdx = ev.getActionIndex();
+                    if (!mSwipeTouchRegion.contains(ev.getX(ptrIdx), ev.getY(ptrIdx))) {
+                        forceCancelGesture(ev);
+                    }
+                }
+                break;
+            }
+            case ACTION_POINTER_UP: {
+                int ptrIdx = ev.getActionIndex();
+                int ptrId = ev.getPointerId(ptrIdx);
+                if (ptrId == mActivePointerId) {
+                    final int newPointerIdx = ptrIdx == 0 ? 1 : 0;
+                    mDownPos.set(
+                            ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x),
+                            ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y));
+                    mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx));
+                    mActivePointerId = ev.getPointerId(newPointerIdx);
+                }
+                break;
+            }
+            case ACTION_MOVE: {
+                int pointerIndex = ev.findPointerIndex(mActivePointerId);
+                if (pointerIndex == INVALID_POINTER_ID) {
+                    break;
+                }
+                mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
+                float displacement = getDisplacement(ev);
+                float displacementX = mLastPos.x - mDownPos.x;
+
+                if (!mPaddedWindowMoveSlop) {
+                    if (!mIsDeferredDownTarget) {
+                        // Normal gesture, ensure we pass the drag slop before we start tracking
+                        // the gesture
+                        if (Math.abs(displacement) > mTouchSlop) {
+                            mPaddedWindowMoveSlop = true;
+                            mStartDisplacement = Math.min(displacement, -mTouchSlop);
+                        }
+                    }
+                }
+
+                if (!mPassedPilferInputSlop) {
+                    float displacementY = mLastPos.y - mDownPos.y;
+                    if (squaredHypot(displacementX, displacementY) >= mSquaredTouchSlop) {
+                        if (mDisableHorizontalSwipe
+                                && Math.abs(displacementX) > Math.abs(displacementY)) {
+                            // Horizontal gesture is not allowed in this region
+                            forceCancelGesture(ev);
+                            break;
+                        }
+
+                        mPassedPilferInputSlop = true;
+
+                        if (mIsDeferredDownTarget) {
+                            // Deferred gesture, start the animation and gesture tracking once
+                            // we pass the actual touch slop
+                            startTouchTrackingForWindowAnimation(ev.getEventTime());
+                        }
+                        if (!mPaddedWindowMoveSlop) {
+                            mPaddedWindowMoveSlop = true;
+                            mStartDisplacement = Math.min(displacement, -mTouchSlop);
+
+                        }
+                        notifyGestureStarted();
+                    }
+                }
+
+                if (mInteractionHandler != null) {
+                    if (mPaddedWindowMoveSlop) {
+                        // Move
+                        mInteractionHandler.updateDisplacement(displacement - mStartDisplacement);
+                    }
+
+                    if (mMode == Mode.NO_BUTTON) {
+                        float horizontalDist = Math.abs(displacementX);
+                        float upDist = -displacement;
+                        boolean isLikelyToStartNewTask = horizontalDist > upDist;
+                        mMotionPauseDetector.setDisallowPause(upDist < mMotionPauseMinDisplacement
+                                || isLikelyToStartNewTask);
+                        mMotionPauseDetector.addPosition(displacement, ev.getEventTime());
+                        mInteractionHandler.setIsLikelyToStartNewTask(isLikelyToStartNewTask);
+                    }
+                }
+                break;
+            }
+            case ACTION_CANCEL:
+            case ACTION_UP: {
+                finishTouchTracking(ev);
+                break;
+            }
+        }
+    }
+
+    private void notifyGestureStarted() {
+        TOUCH_INTERACTION_LOG.addLog("startQuickstep");
+        if (mInteractionHandler == null) {
+            return;
+        }
+        mInputMonitorCompat.pilferPointers();
+
+        mOverviewCallbacks.closeAllWindows();
+        ActivityManagerWrapper.getInstance().closeSystemWindows(
+                CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+
+        // Notify the handler that the gesture has actually started
+        mInteractionHandler.onGestureStarted();
+    }
+
+    private void startTouchTrackingForWindowAnimation(long touchTimeMs) {
+        TOUCH_INTERACTION_LOG.addLog("startRecentsAnimation");
+
+        RecentsAnimationListenerSet listenerSet = mSwipeSharedState.getActiveListener();
+        final WindowTransformSwipeHandler handler = new WindowTransformSwipeHandler(
+                mRunningTask, this, touchTimeMs, mActivityControlHelper,
+                listenerSet != null, mInputConsumer);
+
+        // Preload the plan
+        mRecentsModel.getTasks(null);
+        mInteractionHandler = handler;
+        handler.setGestureEndCallback(this::onInteractionGestureFinished);
+        mMotionPauseDetector.setOnMotionPauseListener(handler::onMotionPauseChanged);
+        handler.initWhenReady();
+
+        if (listenerSet != null) {
+            listenerSet.addListener(handler);
+            mSwipeSharedState.applyActiveRecentsAnimationState(handler);
+            notifyGestureStarted();
+        } else {
+            RecentsAnimationListenerSet newListenerSet =
+                    mSwipeSharedState.newRecentsAnimationListenerSet();
+            newListenerSet.addListener(handler);
+            BackgroundExecutor.get().submit(
+                    () -> ActivityManagerWrapper.getInstance().startRecentsActivity(
+                            mHomeIntent, null, newListenerSet, null, null));
+        }
+    }
+
+    /**
+     * Called when the gesture has ended. Does not correlate to the completion of the interaction as
+     * the animation can still be running.
+     */
+    private void finishTouchTracking(MotionEvent ev) {
+        RaceConditionTracker.onEvent(UP_EVT, ENTER);
+        TraceHelper.endSection("TouchInt");
+
+        if (mPaddedWindowMoveSlop && mInteractionHandler != null) {
+            if (ev.getActionMasked() == ACTION_CANCEL) {
+                mInteractionHandler.onGestureCancelled();
+            } else {
+                mVelocityTracker.computeCurrentVelocity(1000,
+                        ViewConfiguration.get(this).getScaledMaximumFlingVelocity());
+                float velocityX = mVelocityTracker.getXVelocity(mActivePointerId);
+                float velocityY = mVelocityTracker.getYVelocity(mActivePointerId);
+                float velocity = mNavBarPosition.isRightEdge() ? velocityX
+                        : mNavBarPosition.isLeftEdge() ? -velocityX
+                                : velocityY;
+
+                mInteractionHandler.updateDisplacement(getDisplacement(ev) - mStartDisplacement);
+                mInteractionHandler.onGestureEnded(velocity, new PointF(velocityX, velocityY),
+                        mDownPos);
+            }
+        } else {
+            // Since we start touch tracking on DOWN, we may reach this state without actually
+            // starting the gesture. In that case, just cleanup immediately.
+            onConsumerAboutToBeSwitched();
+            onInteractionGestureFinished();
+
+            // Cancel the recents animation if SysUI happens to handle UP before we have a chance
+            // to start the recents animation. In addition, workaround for b/126336729 by delaying
+            // the cancel of the animation for a period, in case SysUI is slow to handle UP and we
+            // handle DOWN & UP and move the home stack before SysUI can start the activity
+            mMainThreadHandler.removeCallbacks(mCancelRecentsAnimationRunnable);
+            mMainThreadHandler.postDelayed(mCancelRecentsAnimationRunnable, 100);
+        }
+        mVelocityTracker.recycle();
+        mVelocityTracker = null;
+        mMotionPauseDetector.clear();
+        RaceConditionTracker.onEvent(UP_EVT, EXIT);
+    }
+
+    @Override
+    public void onConsumerAboutToBeSwitched() {
+        Preconditions.assertUIThread();
+        mMainThreadHandler.removeCallbacks(mCancelRecentsAnimationRunnable);
+        if (mInteractionHandler != null) {
+            // The consumer is being switched while we are active. Set up the shared state to be
+            // used by the next animation
+            removeListener();
+            GestureEndTarget endTarget = mInteractionHandler.getGestureEndTarget();
+            mSwipeSharedState.canGestureBeContinued = endTarget != null && endTarget.canBeContinued;
+            mSwipeSharedState.goingToLauncher = endTarget != null && endTarget.isLauncher;
+            if (mSwipeSharedState.canGestureBeContinued) {
+                mInteractionHandler.cancelCurrentAnimation(mSwipeSharedState);
+            } else {
+                mInteractionHandler.reset();
+            }
+        }
+    }
+
+    @UiThread
+    private void onInteractionGestureFinished() {
+        Preconditions.assertUIThread();
+        removeListener();
+        mInteractionHandler = null;
+        mOnCompleteCallback.accept(this);
+    }
+
+    private void removeListener() {
+        RecentsAnimationListenerSet listenerSet = mSwipeSharedState.getActiveListener();
+        if (listenerSet != null) {
+            listenerSet.removeListener(mInteractionHandler);
+        }
+    }
+
+    private float getDisplacement(MotionEvent ev) {
+        if (mNavBarPosition.isRightEdge()) {
+            return ev.getX() - mDownPos.x;
+        } else if (mNavBarPosition.isLeftEdge()) {
+            return mDownPos.x - ev.getX();
+        } else {
+            return ev.getY() - mDownPos.y;
+        }
+    }
+
+    @Override
+    public boolean useSharedSwipeState() {
+        return mInteractionHandler != null;
+    }
+
+    @Override
+    public boolean allowInterceptByParent() {
+        return !mPassedPilferInputSlop;
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
new file mode 100644
index 0000000..b021df8
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
@@ -0,0 +1,117 @@
+/*
+ * 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 com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG;
+import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
+
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.views.BaseDragLayer;
+import com.android.quickstep.OverviewCallbacks;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.InputMonitorCompat;
+
+import java.util.function.Predicate;
+
+/**
+ * Input consumer for handling touch on the recents/Launcher activity.
+ */
+public class OverviewInputConsumer<T extends BaseDraggingActivity>
+        implements InputConsumer {
+
+    private final T mActivity;
+    private final BaseDragLayer mTarget;
+    private final InputMonitorCompat mInputMonitor;
+
+    private final int[] mLocationOnScreen = new int[2];
+    private final boolean mProxyTouch;
+    private final Predicate<MotionEvent> mEventReceiver;
+
+    private final boolean mStartingInActivityBounds;
+    private boolean mTargetHandledTouch;
+
+    public OverviewInputConsumer(T activity, @Nullable InputMonitorCompat inputMonitor,
+            boolean startingInActivityBounds) {
+        mActivity = activity;
+        mInputMonitor = inputMonitor;
+        mStartingInActivityBounds = startingInActivityBounds;
+
+        mTarget = activity.getDragLayer();
+        if (startingInActivityBounds) {
+            mEventReceiver = mTarget::dispatchTouchEvent;
+            mProxyTouch = true;
+        } else {
+            // Only proxy touches to controllers if we are starting touch from nav bar.
+            mEventReceiver = mTarget::proxyTouchEvent;
+            mTarget.getLocationOnScreen(mLocationOnScreen);
+            mProxyTouch = mTarget.prepareProxyEventStarting();
+        }
+    }
+
+    @Override
+    public int getType() {
+        return TYPE_OVERVIEW;
+    }
+
+    @Override
+    public boolean allowInterceptByParent() {
+        return !mTargetHandledTouch;
+    }
+
+    @Override
+    public void onMotionEvent(MotionEvent ev) {
+        if (!mProxyTouch) {
+            return;
+        }
+
+        int flags = ev.getEdgeFlags();
+        if (!mStartingInActivityBounds) {
+            ev.setEdgeFlags(flags | Utilities.EDGE_NAV_BAR);
+        }
+        ev.offsetLocation(-mLocationOnScreen[0], -mLocationOnScreen[1]);
+        boolean handled = mEventReceiver.test(ev);
+        ev.offsetLocation(mLocationOnScreen[0], mLocationOnScreen[1]);
+        ev.setEdgeFlags(flags);
+
+        if (!mTargetHandledTouch && handled) {
+            mTargetHandledTouch = true;
+            if (!mStartingInActivityBounds) {
+                OverviewCallbacks.get(mActivity).closeAllWindows();
+                ActivityManagerWrapper.getInstance()
+                        .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+                TOUCH_INTERACTION_LOG.addLog("startQuickstep");
+            }
+            if (mInputMonitor != null) {
+                mInputMonitor.pilferPointers();
+            }
+        }
+    }
+
+    @Override
+    public void onKeyEvent(KeyEvent ev) {
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+            mActivity.dispatchKeyEvent(ev);
+        }
+    }
+}
+
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
new file mode 100644
index 0000000..425b8b6
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
@@ -0,0 +1,151 @@
+/*
+ * 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.Utilities.squaredHypot;
+import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG;
+import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
+
+import android.content.Context;
+import android.graphics.PointF;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.ViewConfiguration;
+
+import com.android.launcher3.Utilities;
+import com.android.quickstep.OverviewCallbacks;
+import com.android.quickstep.util.NavBarPosition;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.InputMonitorCompat;
+
+public class OverviewWithoutFocusInputConsumer implements InputConsumer {
+
+    private final InputMonitorCompat mInputMonitor;
+    private final boolean mDisableHorizontalSwipe;
+    private final PointF mDownPos = new PointF();
+    private final float mSquaredTouchSlop;
+    private final Context mContext;
+    private final NavBarPosition mNavBarPosition;
+
+    private boolean mInterceptedTouch;
+    private VelocityTracker mVelocityTracker;
+
+
+    public OverviewWithoutFocusInputConsumer(Context context, InputMonitorCompat inputMonitor,
+            boolean disableHorizontalSwipe) {
+        mInputMonitor = inputMonitor;
+        mDisableHorizontalSwipe = disableHorizontalSwipe;
+        mContext = context;
+        mSquaredTouchSlop = Utilities.squaredTouchSlop(context);
+        mNavBarPosition = new NavBarPosition(context);
+
+        mVelocityTracker = VelocityTracker.obtain();
+    }
+
+    @Override
+    public int getType() {
+        return TYPE_OVERVIEW_WITHOUT_FOCUS;
+    }
+
+    @Override
+    public boolean allowInterceptByParent() {
+        return !mInterceptedTouch;
+    }
+
+    private void endTouchTracking() {
+        if (mVelocityTracker != null) {
+            mVelocityTracker.recycle();
+            mVelocityTracker = null;
+        }
+    }
+
+    @Override
+    public void onMotionEvent(MotionEvent ev) {
+        if (mVelocityTracker == null) {
+            return;
+        }
+
+        mVelocityTracker.addMovement(ev);
+        switch (ev.getActionMasked()) {
+            case ACTION_DOWN: {
+                mDownPos.set(ev.getX(), ev.getY());
+                break;
+            }
+            case ACTION_MOVE: {
+                if (!mInterceptedTouch) {
+                    float displacementX = ev.getX() - mDownPos.x;
+                    float displacementY = ev.getY() - mDownPos.y;
+                    if (squaredHypot(displacementX, displacementY) >= mSquaredTouchSlop) {
+                        if (mDisableHorizontalSwipe
+                                && Math.abs(displacementX) > Math.abs(displacementY)) {
+                            // Horizontal gesture is not allowed in this region
+                            endTouchTracking();
+                            break;
+                        }
+
+                        mInterceptedTouch = true;
+
+                        if (mInputMonitor != null) {
+                            mInputMonitor.pilferPointers();
+                        }
+                    }
+                }
+                break;
+            }
+
+            case ACTION_CANCEL:
+                endTouchTracking();
+                break;
+
+            case ACTION_UP: {
+                finishTouchTracking(ev);
+                endTouchTracking();
+                break;
+            }
+        }
+    }
+
+    private void finishTouchTracking(MotionEvent ev) {
+        mVelocityTracker.computeCurrentVelocity(100);
+        float velocityX = mVelocityTracker.getXVelocity();
+        float velocityY = mVelocityTracker.getYVelocity();
+        float velocity = mNavBarPosition.isRightEdge()
+                ? -velocityX : (mNavBarPosition.isLeftEdge() ? velocityX : -velocityY);
+
+        final boolean triggerQuickstep;
+        if (Math.abs(velocity) >= ViewConfiguration.get(mContext).getScaledMinimumFlingVelocity()) {
+            triggerQuickstep = velocity > 0;
+        } else {
+            float displacementX = mDisableHorizontalSwipe ? 0 : (ev.getX() - mDownPos.x);
+            float displacementY = ev.getY() - mDownPos.y;
+            triggerQuickstep = squaredHypot(displacementX, displacementY) >= mSquaredTouchSlop;
+        }
+
+        if (triggerQuickstep) {
+            OverviewCallbacks.get(mContext).closeAllWindows();
+            ActivityManagerWrapper.getInstance()
+                    .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+            TOUCH_INTERACTION_LOG.addLog("startQuickstep");
+        } else {
+            // ignore
+        }
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java
new file mode 100644
index 0000000..8eede81
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java
@@ -0,0 +1,45 @@
+/*
+ * 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 android.view.MotionEvent;
+
+import com.android.quickstep.SwipeSharedState;
+
+/**
+ * A NO_OP input consumer which also resets any pending gesture
+ */
+public class ResetGestureInputConsumer implements InputConsumer {
+
+    private final SwipeSharedState mSwipeSharedState;
+
+    public ResetGestureInputConsumer(SwipeSharedState swipeSharedState) {
+        mSwipeSharedState = swipeSharedState;
+    }
+
+    @Override
+    public int getType() {
+        return TYPE_RESET_GESTURE;
+    }
+
+    @Override
+    public void onMotionEvent(MotionEvent ev) {
+        if (ev.getAction() == MotionEvent.ACTION_DOWN
+                && mSwipeSharedState.getActiveListener() != null) {
+            mSwipeSharedState.clearAllState(false /* finishAnimation */);
+        }
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
new file mode 100644
index 0000000..a0e20f2
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
@@ -0,0 +1,88 @@
+/*
+ * 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 android.content.Context;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.HapticFeedbackConstants;
+import android.view.MotionEvent;
+
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.R;
+import com.android.quickstep.ActivityControlHelper;
+import com.android.quickstep.util.MotionPauseDetector;
+import com.android.systemui.shared.recents.ISystemUiProxy;
+
+/**
+ * An input consumer that detects swipe up and hold to exit screen pinning mode.
+ */
+public class ScreenPinnedInputConsumer implements InputConsumer {
+
+    private static final String TAG = "ScreenPinnedConsumer";
+
+    private final float mMotionPauseMinDisplacement;
+    private final MotionPauseDetector mMotionPauseDetector;
+
+    private float mTouchDownY;
+
+    public ScreenPinnedInputConsumer(Context context, ISystemUiProxy sysuiProxy,
+            ActivityControlHelper activityControl) {
+        mMotionPauseMinDisplacement = context.getResources().getDimension(
+                R.dimen.motion_pause_detector_min_displacement_from_app);
+        mMotionPauseDetector = new MotionPauseDetector(context, true /* makePauseHarderToTrigger*/);
+        mMotionPauseDetector.setOnMotionPauseListener(isPaused -> {
+            if (isPaused) {
+                try {
+                    sysuiProxy.stopScreenPinning();
+                    BaseDraggingActivity launcherActivity = activityControl.getCreatedActivity();
+                    if (launcherActivity != null) {
+                        launcherActivity.getRootView().performHapticFeedback(
+                                HapticFeedbackConstants.LONG_PRESS,
+                                HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+                    }
+                    mMotionPauseDetector.clear();
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Unable to stop screen pinning ", e);
+                }
+            }
+        });
+    }
+
+    @Override
+    public int getType() {
+        return TYPE_SCREEN_PINNED;
+    }
+
+    @Override
+    public void onMotionEvent(MotionEvent ev) {
+        float y = ev.getY();
+        switch (ev.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+                mTouchDownY = y;
+                break;
+            case MotionEvent.ACTION_MOVE:
+                float displacement = mTouchDownY - y;
+                mMotionPauseDetector.setDisallowPause(displacement < mMotionPauseMinDisplacement);
+                mMotionPauseDetector.addPosition(y, ev.getEventTime());
+                break;
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_UP:
+                mMotionPauseDetector.clear();
+                break;
+        }
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java
new file mode 100644
index 0000000..6dc672e
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java
@@ -0,0 +1,420 @@
+/*
+ * 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.util;
+
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+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.annotation.TargetApi;
+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.os.RemoteException;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.views.BaseDragLayer;
+import com.android.quickstep.RecentsModel;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskThumbnailView;
+import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.recents.ISystemUiProxy;
+import com.android.systemui.shared.recents.utilities.RectFEvaluator;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
+import com.android.systemui.shared.system.TransactionCompat;
+import com.android.systemui.shared.system.WindowManagerWrapper;
+
+import java.util.function.BiFunction;
+
+/**
+ * Utility class to handle window clip animation
+ */
+@TargetApi(Build.VERSION_CODES.P)
+public class ClipAnimationHelper {
+
+    // The bounds of the source app in device coordinates
+    private final Rect mSourceStackBounds = new Rect();
+    // The insets of the source app
+    private final Rect mSourceInsets = new Rect();
+    // The source app bounds with the source insets applied, in the source app window coordinates
+    private final RectF mSourceRect = new RectF();
+    // The bounds of the task view in launcher window coordinates
+    private final RectF mTargetRect = new RectF();
+    // The insets to be used for clipping the app window, which can be larger than mSourceInsets
+    // if the aspect ratio of the target is smaller than the aspect ratio of the source rect. In
+    // app window coordinates.
+    private final RectF mSourceWindowClipInsets = new RectF();
+    // The insets to be used for clipping the app window. For live tile, we don't transform the clip
+    // relative to the target rect.
+    private final RectF mSourceWindowClipInsetsForLiveTile = new RectF();
+
+    // The bounds of launcher (not including insets) in device coordinates
+    public final Rect mHomeStackBounds = new Rect();
+
+    // The clip rect in source app window coordinates
+    private final RectF mClipRectF = new RectF();
+    private final RectFEvaluator mRectFEvaluator = new RectFEvaluator();
+    private final Matrix mTmpMatrix = new Matrix();
+    private final Rect mTmpRect = new Rect();
+    private final RectF mTmpRectF = new RectF();
+    private final RectF mCurrentRectWithInsets = new RectF();
+    // Corner radius of windows, in pixels
+    private final float mWindowCornerRadius;
+    // Corner radius of windows when they're in overview mode.
+    private final float mTaskCornerRadius;
+    // If windows can have real time rounded corners.
+    private final boolean mSupportsRoundedCornersOnWindows;
+    // Whether or not to actually use the rounded cornders on windows
+    private boolean mUseRoundedCornersOnWindows;
+
+    // Corner radius currently applied to transformed window.
+    private float mCurrentCornerRadius;
+
+    // Whether to boost the opening animation target layers, or the closing
+    private int mBoostModeTargetLayers = -1;
+
+    private BiFunction<RemoteAnimationTargetCompat, Float, Float> mTaskAlphaCallback =
+            (t, a1) -> a1;
+
+    public ClipAnimationHelper(Context context) {
+        mWindowCornerRadius = getWindowCornerRadius(context.getResources());
+        mSupportsRoundedCornersOnWindows = supportsRoundedCornersOnWindows(context.getResources());
+        mTaskCornerRadius = TaskCornerRadius.get(context);
+        mUseRoundedCornersOnWindows = mSupportsRoundedCornersOnWindows;
+    }
+
+    private void updateSourceStack(RemoteAnimationTargetCompat target) {
+        mSourceInsets.set(target.contentInsets);
+        mSourceStackBounds.set(target.sourceContainerBounds);
+
+        // TODO: Should sourceContainerBounds already have this offset?
+        mSourceStackBounds.offsetTo(target.position.x, target.position.y);
+
+    }
+
+    public void updateSource(Rect homeStackBounds, RemoteAnimationTargetCompat target) {
+        mHomeStackBounds.set(homeStackBounds);
+        updateSourceStack(target);
+    }
+
+    public void updateTargetRect(Rect targetRect) {
+        mSourceRect.set(mSourceInsets.left, mSourceInsets.top,
+                mSourceStackBounds.width() - mSourceInsets.right,
+                mSourceStackBounds.height() - mSourceInsets.bottom);
+        mTargetRect.set(targetRect);
+        mTargetRect.offset(mHomeStackBounds.left - mSourceStackBounds.left,
+                mHomeStackBounds.top - mSourceStackBounds.top);
+
+        // Calculate the clip based on the target rect (since the content insets and the
+        // launcher insets may differ, so the aspect ratio of the target rect can differ
+        // from the source rect. The difference between the target rect (scaled to the
+        // source rect) is the amount to clip on each edge.
+        RectF scaledTargetRect = new RectF(mTargetRect);
+        Utilities.scaleRectFAboutCenter(scaledTargetRect,
+                mSourceRect.width() / mTargetRect.width());
+        scaledTargetRect.offsetTo(mSourceRect.left, mSourceRect.top);
+        mSourceWindowClipInsets.set(
+                Math.max(scaledTargetRect.left, 0),
+                Math.max(scaledTargetRect.top, 0),
+                Math.max(mSourceStackBounds.width() - scaledTargetRect.right, 0),
+                Math.max(mSourceStackBounds.height() - scaledTargetRect.bottom, 0));
+        mSourceWindowClipInsetsForLiveTile.set(mSourceWindowClipInsets);
+        mSourceRect.set(scaledTargetRect);
+    }
+
+    public void prepareAnimation(DeviceProfile dp, boolean isOpening) {
+        mBoostModeTargetLayers = isOpening ? MODE_OPENING : MODE_CLOSING;
+        mUseRoundedCornersOnWindows = mSupportsRoundedCornersOnWindows && !dp.isMultiWindowMode;
+    }
+
+    public RectF applyTransform(RemoteAnimationTargetSet targetSet, TransformParams params) {
+        return applyTransform(targetSet, params, true /* launcherOnTop */);
+    }
+
+    public RectF applyTransform(RemoteAnimationTargetSet targetSet, TransformParams params,
+            boolean launcherOnTop) {
+        float progress = params.progress;
+        if (params.currentRect == null) {
+            RectF currentRect;
+            mTmpRectF.set(mTargetRect);
+            Utilities.scaleRectFAboutCenter(mTmpRectF, params.offsetScale);
+            currentRect = mRectFEvaluator.evaluate(progress, mSourceRect, mTmpRectF);
+            currentRect.offset(params.offsetX, 0);
+
+            // Don't clip past progress > 1.
+            progress = Math.min(1, progress);
+            final RectF sourceWindowClipInsets = params.forLiveTile
+                    ? mSourceWindowClipInsetsForLiveTile : mSourceWindowClipInsets;
+            mClipRectF.left = sourceWindowClipInsets.left * progress;
+            mClipRectF.top = sourceWindowClipInsets.top * progress;
+            mClipRectF.right =
+                    mSourceStackBounds.width() - (sourceWindowClipInsets.right * progress);
+            mClipRectF.bottom =
+                    mSourceStackBounds.height() - (sourceWindowClipInsets.bottom * progress);
+            params.setCurrentRectAndTargetAlpha(currentRect, 1);
+        }
+
+        SurfaceParams[] surfaceParams = new SurfaceParams[targetSet.unfilteredApps.length];
+        for (int i = 0; i < targetSet.unfilteredApps.length; i++) {
+            RemoteAnimationTargetCompat app = targetSet.unfilteredApps[i];
+            mTmpMatrix.setTranslate(app.position.x, app.position.y);
+            Rect crop = mTmpRect;
+            crop.set(app.sourceContainerBounds);
+            crop.offsetTo(0, 0);
+            float alpha = 1f;
+            int layer = RemoteAnimationProvider.getLayer(app, mBoostModeTargetLayers);
+            float cornerRadius = 0f;
+            float scale = Math.max(params.currentRect.width(), mTargetRect.width()) / crop.width();
+            if (app.mode == targetSet.targetMode) {
+                alpha = mTaskAlphaCallback.apply(app, params.targetAlpha);
+                if (app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
+                    mTmpMatrix.setRectToRect(mSourceRect, params.currentRect, ScaleToFit.FILL);
+                    mTmpMatrix.postTranslate(app.position.x, app.position.y);
+                    mClipRectF.roundOut(crop);
+                    if (mSupportsRoundedCornersOnWindows) {
+                        if (params.cornerRadius > -1) {
+                            cornerRadius = params.cornerRadius;
+                            scale = params.currentRect.width() / crop.width();
+                        } else {
+                            float windowCornerRadius = mUseRoundedCornersOnWindows
+                                    ? mWindowCornerRadius : 0;
+                            cornerRadius = Utilities.mapRange(progress, windowCornerRadius,
+                                    mTaskCornerRadius);
+                        }
+                        mCurrentCornerRadius = cornerRadius;
+                    }
+                } else if (targetSet.hasRecents) {
+                    // If home has a different target then recents, reverse anim the
+                    // home target.
+                    alpha = 1 - (progress * params.targetAlpha);
+                }
+            } else if (ENABLE_QUICKSTEP_LIVE_TILE.get() && launcherOnTop) {
+                crop = null;
+                layer = Integer.MAX_VALUE;
+            }
+
+            // Since radius is in Surface space, but we draw the rounded corners in screen space, we
+            // have to undo the scale.
+            surfaceParams[i] = new SurfaceParams(app.leash, alpha, mTmpMatrix, crop, layer,
+                    cornerRadius / scale);
+        }
+        applySurfaceParams(params.syncTransactionApplier, surfaceParams);
+        return params.currentRect;
+    }
+
+    public RectF getCurrentRectWithInsets() {
+        mTmpMatrix.mapRect(mCurrentRectWithInsets, mClipRectF);
+        return mCurrentRectWithInsets;
+    }
+
+    private void applySurfaceParams(@Nullable SyncRtSurfaceTransactionApplierCompat
+            syncTransactionApplier, SurfaceParams[] params) {
+        if (syncTransactionApplier != null) {
+            syncTransactionApplier.scheduleApply(params);
+        } else {
+            TransactionCompat t = new TransactionCompat();
+            for (SurfaceParams param : params) {
+                SyncRtSurfaceTransactionApplierCompat.applyParams(t, param);
+            }
+            t.setEarlyWakeup();
+            t.apply();
+        }
+    }
+
+    public void setTaskAlphaCallback(
+            BiFunction<RemoteAnimationTargetCompat, Float, Float> callback) {
+        mTaskAlphaCallback = callback;
+    }
+
+    public void fromTaskThumbnailView(TaskThumbnailView ttv, RecentsView rv) {
+        fromTaskThumbnailView(ttv, rv, null);
+    }
+
+    public void fromTaskThumbnailView(TaskThumbnailView ttv, RecentsView rv,
+            @Nullable RemoteAnimationTargetCompat target) {
+        BaseDraggingActivity activity = BaseDraggingActivity.fromContext(ttv.getContext());
+        BaseDragLayer dl = activity.getDragLayer();
+
+        int[] pos = new int[2];
+        dl.getLocationOnScreen(pos);
+        mHomeStackBounds.set(0, 0, dl.getWidth(), dl.getHeight());
+        mHomeStackBounds.offset(pos[0], pos[1]);
+
+        if (target != null) {
+            updateSourceStack(target);
+        } else  if (rv.shouldUseMultiWindowTaskSizeStrategy()) {
+            updateStackBoundsToMultiWindowTaskSize(activity);
+        } else {
+            mSourceStackBounds.set(mHomeStackBounds);
+            Rect fallback = dl.getInsets();
+            mSourceInsets.set(ttv.getInsets(fallback));
+        }
+
+        Rect targetRect = new Rect();
+        dl.getDescendantRectRelativeToSelf(ttv, targetRect);
+        updateTargetRect(targetRect);
+
+        if (target == null) {
+            // Transform the clip relative to the target rect. Only do this in the case where we
+            // aren't applying the insets to the app windows (where the clip should be in target app
+            // space)
+            float scale = mTargetRect.width() / mSourceRect.width();
+            mSourceWindowClipInsets.left = mSourceWindowClipInsets.left * scale;
+            mSourceWindowClipInsets.top = mSourceWindowClipInsets.top * scale;
+            mSourceWindowClipInsets.right = mSourceWindowClipInsets.right * scale;
+            mSourceWindowClipInsets.bottom = mSourceWindowClipInsets.bottom * scale;
+        }
+    }
+
+    /**
+     * Compute scale and translation y such that the specified task view fills the screen.
+     */
+    public ClipAnimationHelper updateForFullscreenOverview(TaskView v) {
+        TaskThumbnailView thumbnailView = v.getThumbnail();
+        RecentsView recentsView = v.getRecentsView();
+        fromTaskThumbnailView(thumbnailView, recentsView);
+        Rect taskSize = new Rect();
+        recentsView.getTaskSize(taskSize);
+        updateTargetRect(taskSize);
+        return this;
+    }
+
+    /**
+     * @return The source rect's scale and translation relative to the target rect.
+     */
+    public LauncherState.ScaleAndTranslation getScaleAndTranslation() {
+        float scale = mSourceRect.width() / mTargetRect.width();
+        float translationY = mSourceRect.centerY() - mSourceRect.top - mTargetRect.centerY();
+        return new LauncherState.ScaleAndTranslation(scale, 0, translationY);
+    }
+
+    private void updateStackBoundsToMultiWindowTaskSize(BaseDraggingActivity activity) {
+        ISystemUiProxy sysUiProxy = RecentsModel.INSTANCE.get(activity).getSystemUiProxy();
+        if (sysUiProxy != null) {
+            try {
+                mSourceStackBounds.set(sysUiProxy.getNonMinimizedSplitScreenSecondaryBounds());
+                return;
+            } catch (RemoteException e) {
+                // Use half screen size
+            }
+        }
+
+        // Assume that the task size is half screen size (minus the insets and the divider size)
+        DeviceProfile fullDp = activity.getDeviceProfile().getFullScreenProfile();
+        // Use availableWidthPx and availableHeightPx instead of widthPx and heightPx to
+        // account for system insets
+        int taskWidth = fullDp.availableWidthPx;
+        int taskHeight = fullDp.availableHeightPx;
+        int halfDividerSize = activity.getResources()
+                .getDimensionPixelSize(R.dimen.multi_window_task_divider_size) / 2;
+
+        Rect insets = new Rect();
+        WindowManagerWrapper.getInstance().getStableInsets(insets);
+        if (fullDp.isLandscape) {
+            taskWidth = taskWidth / 2 - halfDividerSize;
+        } else {
+            taskHeight = taskHeight / 2 - halfDividerSize;
+        }
+
+        // Align the task to bottom left/right edge (closer to nav bar).
+        int left = activity.getDeviceProfile().isSeascape() ? insets.left
+                : (insets.left + fullDp.availableWidthPx - taskWidth);
+        mSourceStackBounds.set(0, 0, taskWidth, taskHeight);
+        mSourceStackBounds.offset(left, insets.top + fullDp.availableHeightPx - taskHeight);
+    }
+
+    public RectF getTargetRect() {
+        return mTargetRect;
+    }
+
+    public float getCurrentCornerRadius() {
+        return mCurrentCornerRadius;
+    }
+
+    public static class TransformParams {
+        float progress;
+        public float offsetX;
+        public float offsetScale;
+        @Nullable RectF currentRect;
+        float targetAlpha;
+        boolean forLiveTile;
+        float cornerRadius;
+
+        SyncRtSurfaceTransactionApplierCompat syncTransactionApplier;
+
+        public TransformParams() {
+            progress = 0;
+            offsetX = 0;
+            offsetScale = 1;
+            currentRect = null;
+            targetAlpha = 0;
+            forLiveTile = false;
+            cornerRadius = -1;
+        }
+
+        public TransformParams setProgress(float progress) {
+            this.progress = progress;
+            this.currentRect = null;
+            return this;
+        }
+
+        public TransformParams setCornerRadius(float cornerRadius) {
+            this.cornerRadius = cornerRadius;
+            return this;
+        }
+
+        public TransformParams setCurrentRectAndTargetAlpha(RectF currentRect, float targetAlpha) {
+            this.currentRect = currentRect;
+            this.targetAlpha = targetAlpha;
+            return this;
+        }
+
+        public TransformParams setOffsetX(float offsetX) {
+            this.offsetX = offsetX;
+            return this;
+        }
+
+        public TransformParams setOffsetScale(float offsetScale) {
+            this.offsetScale = offsetScale;
+            return this;
+        }
+
+        public TransformParams setForLiveTile(boolean forLiveTile) {
+            this.forLiveTile = forLiveTile;
+            return this;
+        }
+
+        public TransformParams setSyncTransactionApplier(
+                SyncRtSurfaceTransactionApplierCompat applier) {
+            this.syncTransactionApplier = applier;
+            return this;
+        }
+    }
+}
+
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/NavBarPosition.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/NavBarPosition.java
new file mode 100644
index 0000000..3ce341d
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/NavBarPosition.java
@@ -0,0 +1,55 @@
+/*
+ * 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.uioverrides.RecentsUiFactory.ROTATION_LANDSCAPE;
+import static com.android.launcher3.uioverrides.RecentsUiFactory.ROTATION_SEASCAPE;
+import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
+
+import android.content.Context;
+import android.view.Surface;
+import android.view.WindowManager;
+
+import com.android.launcher3.graphics.RotationMode;
+import com.android.quickstep.SysUINavigationMode;
+
+/**
+ * Utility class to check nav bar position
+ */
+public class NavBarPosition {
+
+    private final SysUINavigationMode.Mode mMode;
+    private final int mDisplayRotation;
+
+    public NavBarPosition(Context context) {
+        mMode = SysUINavigationMode.getMode(context);
+        mDisplayRotation = context.getSystemService(WindowManager.class)
+                .getDefaultDisplay().getRotation();
+    }
+
+    public boolean isRightEdge() {
+        return mMode != NO_BUTTON && mDisplayRotation == Surface.ROTATION_90;
+    }
+
+    public boolean isLeftEdge() {
+        return mMode != NO_BUTTON && mDisplayRotation == Surface.ROTATION_270;
+    }
+
+    public RotationMode getRotationMode() {
+        return isLeftEdge() ? ROTATION_SEASCAPE
+                : (isRightEdge() ? ROTATION_LANDSCAPE : RotationMode.NORMAL);
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationListenerSet.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationListenerSet.java
new file mode 100644
index 0000000..83601e6
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationListenerSet.java
@@ -0,0 +1,114 @@
+/*
+ * 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.quickstep.TouchInteractionService.MAIN_THREAD_EXECUTOR;
+
+import android.graphics.Rect;
+import android.util.ArraySet;
+
+import com.android.launcher3.Utilities;
+import com.android.launcher3.util.Preconditions;
+import com.android.quickstep.util.SwipeAnimationTargetSet.SwipeAnimationListener;
+import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
+import com.android.systemui.shared.system.RecentsAnimationListener;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+import java.util.Set;
+import java.util.function.Consumer;
+
+import androidx.annotation.UiThread;
+
+/**
+ * Wrapper around {@link RecentsAnimationListener} which delegates callbacks to multiple listeners
+ * on the main thread
+ */
+public class RecentsAnimationListenerSet implements RecentsAnimationListener {
+
+    // The actual app surface is replaced by a screenshot upon recents animation cancelation when
+    // deferredWithScreenshot is true. Launcher takes the responsibility to clean up this screenshot
+    // after app transition is finished. This delay is introduced to cover the app transition
+    // period of time.
+    private final int TRANSITION_DELAY = 100;
+
+    private final Set<SwipeAnimationListener> mListeners = new ArraySet<>();
+    private final boolean mShouldMinimizeSplitScreen;
+    private final Consumer<SwipeAnimationTargetSet> mOnFinishListener;
+    private RecentsAnimationControllerCompat mController;
+
+    private boolean mCancelled;
+
+    public RecentsAnimationListenerSet(boolean shouldMinimizeSplitScreen,
+            Consumer<SwipeAnimationTargetSet> onFinishListener) {
+        mShouldMinimizeSplitScreen = shouldMinimizeSplitScreen;
+        mOnFinishListener = onFinishListener;
+    }
+
+    @UiThread
+    public void addListener(SwipeAnimationListener listener) {
+        Preconditions.assertUIThread();
+        mListeners.add(listener);
+    }
+
+    @UiThread
+    public void removeListener(SwipeAnimationListener listener) {
+        Preconditions.assertUIThread();
+        mListeners.remove(listener);
+    }
+
+    @Override
+    public final void onAnimationStart(RecentsAnimationControllerCompat controller,
+            RemoteAnimationTargetCompat[] targets, Rect homeContentInsets,
+            Rect minimizedHomeBounds) {
+        mController = controller;
+        SwipeAnimationTargetSet targetSet = new SwipeAnimationTargetSet(controller, targets,
+                homeContentInsets, minimizedHomeBounds, mShouldMinimizeSplitScreen,
+                mOnFinishListener);
+
+        if (mCancelled) {
+            targetSet.cancelAnimation();
+        } else {
+            Utilities.postAsyncCallback(MAIN_THREAD_EXECUTOR.getHandler(), () -> {
+                for (SwipeAnimationListener listener : getListeners()) {
+                    listener.onRecentsAnimationStart(targetSet);
+                }
+            });
+        }
+    }
+
+    @Override
+    public final void onAnimationCanceled(boolean deferredWithScreenshot) {
+        Utilities.postAsyncCallback(MAIN_THREAD_EXECUTOR.getHandler(), () -> {
+            for (SwipeAnimationListener listener : getListeners()) {
+                listener.onRecentsAnimationCanceled();
+            }
+        });
+        // TODO: handle the transition better instead of simply using a transition delay.
+        if (deferredWithScreenshot) {
+            MAIN_THREAD_EXECUTOR.getHandler().postDelayed(() -> mController.cleanupScreenshot(),
+                    TRANSITION_DELAY);
+        }
+    }
+
+    private SwipeAnimationListener[] getListeners() {
+        return mListeners.toArray(new SwipeAnimationListener[mListeners.size()]);
+    }
+
+    public void cancelListener() {
+        mCancelled = true;
+        onAnimationCanceled(false);
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java
new file mode 100644
index 0000000..77dc6f3
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java
@@ -0,0 +1,231 @@
+/*
+ * 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 android.animation.Animator;
+import android.content.res.Resources;
+import android.graphics.PointF;
+import android.graphics.RectF;
+
+import androidx.dynamicanimation.animation.DynamicAnimation.OnAnimationEndListener;
+import androidx.dynamicanimation.animation.FloatPropertyCompat;
+import androidx.dynamicanimation.animation.SpringAnimation;
+import androidx.dynamicanimation.animation.SpringForce;
+
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.FlingSpringAnim;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * Applies spring forces to animate from a starting rect to a target rect,
+ * while providing update callbacks to the caller.
+ */
+public class RectFSpringAnim {
+
+    private static final FloatPropertyCompat<RectFSpringAnim> RECT_CENTER_X =
+            new FloatPropertyCompat<RectFSpringAnim>("rectCenterXSpring") {
+                @Override
+                public float getValue(RectFSpringAnim anim) {
+                    return anim.mCurrentCenterX;
+                }
+
+                @Override
+                public void setValue(RectFSpringAnim anim, float currentCenterX) {
+                    anim.mCurrentCenterX = currentCenterX;
+                    anim.onUpdate();
+                }
+            };
+
+    private static final FloatPropertyCompat<RectFSpringAnim> RECT_Y =
+            new FloatPropertyCompat<RectFSpringAnim>("rectYSpring") {
+                @Override
+                public float getValue(RectFSpringAnim anim) {
+                    return anim.mCurrentY;
+                }
+
+                @Override
+                public void setValue(RectFSpringAnim anim, float y) {
+                    anim.mCurrentY = y;
+                    anim.onUpdate();
+                }
+            };
+
+    private static final FloatPropertyCompat<RectFSpringAnim> RECT_SCALE_PROGRESS =
+            new FloatPropertyCompat<RectFSpringAnim>("rectScaleProgress") {
+                @Override
+                public float getValue(RectFSpringAnim object) {
+                    return object.mCurrentScaleProgress;
+                }
+
+                @Override
+                public void setValue(RectFSpringAnim object, float value) {
+                    object.mCurrentScaleProgress = value;
+                    object.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 mCurrentY;
+    // If true, tracking the bottom of the rects, else tracking the top.
+    private boolean mTrackingBottomY;
+    private float mCurrentScaleProgress;
+    private FlingSpringAnim mRectXAnim;
+    private FlingSpringAnim mRectYAnim;
+    private SpringAnimation mRectScaleAnim;
+    private boolean mAnimsStarted;
+    private boolean mRectXAnimEnded;
+    private boolean mRectYAnimEnded;
+    private boolean mRectScaleAnimEnded;
+
+    private float mMinVisChange;
+    private float mYOvershoot;
+
+    public RectFSpringAnim(RectF startRect, RectF targetRect, Resources resources) {
+        mStartRect = startRect;
+        mTargetRect = targetRect;
+        mCurrentCenterX = mStartRect.centerX();
+
+        mTrackingBottomY = startRect.bottom < targetRect.bottom;
+        mCurrentY = mTrackingBottomY ? mStartRect.bottom : mStartRect.top;
+
+        mMinVisChange = resources.getDimensionPixelSize(R.dimen.swipe_up_fling_min_visible_change);
+        mYOvershoot = resources.getDimensionPixelSize(R.dimen.swipe_up_y_overshoot);
+    }
+
+    public void onTargetPositionChanged() {
+        if (mRectXAnim != null && mRectXAnim.getTargetPosition() != mTargetRect.centerX()) {
+            mRectXAnim.updatePosition(mCurrentCenterX, mTargetRect.centerX());
+        }
+
+        if (mRectYAnim != null) {
+            if (mTrackingBottomY && mRectYAnim.getTargetPosition() != mTargetRect.bottom) {
+                mRectYAnim.updatePosition(mCurrentY, mTargetRect.bottom);
+            } else if (!mTrackingBottomY && mRectYAnim.getTargetPosition() != mTargetRect.top) {
+                mRectYAnim.updatePosition(mCurrentY, mTargetRect.top);
+            }
+        }
+    }
+
+    public void addOnUpdateListener(OnUpdateListener onUpdateListener) {
+        mOnUpdateListeners.add(onUpdateListener);
+    }
+
+    public void addAnimatorListener(Animator.AnimatorListener animatorListener) {
+        mAnimatorListeners.add(animatorListener);
+    }
+
+    public void start(PointF velocityPxPerMs) {
+        // Only tell caller that we ended if both x and y animations have ended.
+        OnAnimationEndListener onXEndListener = ((animation, canceled, centerX, velocityX) -> {
+            mRectXAnimEnded = true;
+            maybeOnEnd();
+        });
+        OnAnimationEndListener onYEndListener = ((animation, canceled, centerY, velocityY) -> {
+            mRectYAnimEnded = true;
+            maybeOnEnd();
+        });
+
+        float startX = mCurrentCenterX;
+        float endX = mTargetRect.centerX();
+        float minXValue = Math.min(startX, endX);
+        float maxXValue = Math.max(startX, endX);
+        mRectXAnim = new FlingSpringAnim(this, RECT_CENTER_X, startX, endX,
+                velocityPxPerMs.x * 1000, mMinVisChange, minXValue, maxXValue, 1f, onXEndListener);
+
+        float startVelocityY = velocityPxPerMs.y * 1000;
+        // Scale the Y velocity based on the initial velocity to tune the curves.
+        float springVelocityFactor = 0.1f + 0.9f * Math.abs(startVelocityY) / 20000.0f;
+        float startY = mCurrentY;
+        float endY = mTrackingBottomY ? mTargetRect.bottom : mTargetRect.top;
+        float minYValue = Math.min(startY, endY - mYOvershoot);
+        float maxYValue = Math.max(startY, endY);
+        mRectYAnim = new FlingSpringAnim(this, RECT_Y, startY, endY, startVelocityY,
+                mMinVisChange, minYValue, maxYValue, springVelocityFactor, onYEndListener);
+
+        float minVisibleChange = 1f / mStartRect.height();
+        mRectScaleAnim = new SpringAnimation(this, RECT_SCALE_PROGRESS)
+                .setSpring(new SpringForce(1f)
+                .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
+                .setStiffness(SpringForce.STIFFNESS_LOW))
+                .setStartVelocity(velocityPxPerMs.y * minVisibleChange)
+                .setMaxValue(1f)
+                .setMinimumVisibleChange(minVisibleChange)
+                .addEndListener((animation, canceled, value, velocity) -> {
+                    mRectScaleAnimEnded = true;
+                    maybeOnEnd();
+                });
+
+        mRectXAnim.start();
+        mRectYAnim.start();
+        mRectScaleAnim.start();
+        mAnimsStarted = true;
+        for (Animator.AnimatorListener animatorListener : mAnimatorListeners) {
+            animatorListener.onAnimationStart(null);
+        }
+    }
+
+    public void end() {
+        if (mAnimsStarted) {
+            mRectXAnim.end();
+            mRectYAnim.end();
+            if (mRectScaleAnim.canSkipToEnd()) {
+                mRectScaleAnim.skipToEnd();
+            }
+        }
+    }
+
+    private void onUpdate() {
+        if (!mOnUpdateListeners.isEmpty()) {
+            float currentWidth = Utilities.mapRange(mCurrentScaleProgress, mStartRect.width(),
+                    mTargetRect.width());
+            float currentHeight = Utilities.mapRange(mCurrentScaleProgress, mStartRect.height(),
+                    mTargetRect.height());
+            if (mTrackingBottomY) {
+                mCurrentRect.set(mCurrentCenterX - currentWidth / 2, mCurrentY - currentHeight,
+                        mCurrentCenterX + currentWidth / 2, mCurrentY);
+            } else {
+                mCurrentRect.set(mCurrentCenterX - currentWidth / 2, mCurrentY,
+                        mCurrentCenterX + currentWidth / 2, mCurrentY + currentHeight);
+            }
+            for (OnUpdateListener onUpdateListener : mOnUpdateListeners) {
+                onUpdateListener.onUpdate(mCurrentRect, mCurrentScaleProgress);
+            }
+        }
+    }
+
+    private void maybeOnEnd() {
+        if (mAnimsStarted && mRectXAnimEnded && mRectYAnimEnded && mRectScaleAnimEnded) {
+            mAnimsStarted = false;
+            for (Animator.AnimatorListener animatorListener : mAnimatorListeners) {
+                animatorListener.onAnimationEnd(null);
+            }
+        }
+    }
+
+    public interface OnUpdateListener {
+        void onUpdate(RectF currentRect, float progress);
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
new file mode 100644
index 0000000..07e9686
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -0,0 +1,169 @@
+/*
+ * 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 android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager.AnimationConfig;
+import com.android.launcher3.R;
+import com.android.launcher3.ShortcutAndWidgetContainer;
+import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.anim.SpringObjectAnimator;
+
+import java.util.ArrayList;
+import java.util.List;
+
+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.LINEAR;
+
+/**
+ * Creates an animation where all the workspace items are moved into their final location,
+ * staggered row by row from the bottom up.
+ * This is used in conjunction with the swipe up to home animation.
+ */
+public class StaggeredWorkspaceAnim {
+
+    private static final int APP_CLOSE_ROW_START_DELAY_MS = 10;
+    private static final int ALPHA_DURATION_MS = 250;
+
+    private static final float MAX_VELOCITY_PX_PER_S = 22f;
+
+    private static final float DAMPING_RATIO = 0.7f;
+    private static final float STIFFNESS = 150f;
+
+    private final float mVelocity;
+    private final float mSpringTransY;
+    private final View mViewToIgnore;
+
+    private final List<Animator> mAnimators = new ArrayList<>();
+
+    /**
+     * @param floatingViewOriginalView The FloatingIconView's original view.
+     */
+    public StaggeredWorkspaceAnim(Launcher launcher, @Nullable View floatingViewOriginalView,
+            float velocity) {
+        mVelocity = velocity;
+        // We ignore this view since it's visibility and position is controlled by
+        // the FloatingIconView.
+        mViewToIgnore = floatingViewOriginalView;
+
+        // Scale the translationY based on the initial velocity to better sync the workspace items
+        // with the floating view.
+        float transFactor = 0.2f + 0.9f * Math.abs(velocity) / MAX_VELOCITY_PX_PER_S;
+        mSpringTransY = transFactor * launcher.getResources()
+                .getDimensionPixelSize(R.dimen.swipe_up_max_workspace_trans_y);
+
+        DeviceProfile grid = launcher.getDeviceProfile();
+        ShortcutAndWidgetContainer currentPage = ((CellLayout) launcher.getWorkspace()
+                .getChildAt(launcher.getWorkspace().getCurrentPage()))
+                .getShortcutsAndWidgets();
+
+        // Hotseat and QSB takes up two additional rows.
+        int totalRows = grid.inv.numRows + (grid.isVerticalBarLayout() ? 0 : 2);
+
+        // Set up springs on workspace items.
+        for (int i = currentPage.getChildCount() - 1; i >= 0; i--) {
+            View child = currentPage.getChildAt(i);
+            CellLayout.LayoutParams lp = ((CellLayout.LayoutParams) child.getLayoutParams());
+            addStaggeredAnimationForView(child, lp.cellY + lp.cellVSpan, totalRows);
+        }
+
+        // Set up springs for the hotseat and qsb.
+        if (grid.isVerticalBarLayout()) {
+            ViewGroup hotseat = (ViewGroup) launcher.getHotseat().getChildAt(0);
+            for (int i = hotseat.getChildCount() - 1; i >= 0; i--) {
+                View child = hotseat.getChildAt(i);
+                CellLayout.LayoutParams lp = ((CellLayout.LayoutParams) child.getLayoutParams());
+                addStaggeredAnimationForView(child, lp.cellY + 1, totalRows);
+            }
+        } else {
+            View hotseat = launcher.getHotseat().getChildAt(0);
+            addStaggeredAnimationForView(hotseat, grid.inv.numRows + 1, totalRows);
+
+            View qsb = launcher.findViewById(R.id.search_container_all_apps);
+            addStaggeredAnimationForView(qsb, grid.inv.numRows + 2, totalRows);
+        }
+
+        addWorkspaceScrimAnimationForState(launcher, BACKGROUND_APP, 0);
+        addWorkspaceScrimAnimationForState(launcher, NORMAL, ALPHA_DURATION_MS);
+    }
+
+    /**
+     * Starts the animation.
+     */
+    public void start() {
+        for (Animator a : mAnimators) {
+            if (a instanceof SpringObjectAnimator) {
+                ((SpringObjectAnimator) a).startSpring(1f, mVelocity, null);
+            } else {
+                a.start();
+            }
+        }
+    }
+
+    /**
+     * Adds an alpha/trans animator for {@param v}, with a start delay based on the view's row.
+     *
+     * @param v A view on the workspace.
+     * @param row The bottom-most row that contains the view.
+     * @param totalRows Total number of rows.
+     */
+    private void addStaggeredAnimationForView(View v, int row, int totalRows) {
+        if (v == mViewToIgnore) {
+            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.
+        long startDelay = (long) ((invertedRow + 1) * APP_CLOSE_ROW_START_DELAY_MS);
+
+        v.setTranslationY(mSpringTransY);
+        SpringObjectAnimator springTransY = new SpringObjectAnimator<>(v, VIEW_TRANSLATE_Y,
+                1f, DAMPING_RATIO, STIFFNESS, mSpringTransY, 0);
+        springTransY.setStartDelay(startDelay);
+        mAnimators.add(springTransY);
+
+        v.setAlpha(0);
+        ObjectAnimator alpha = ObjectAnimator.ofFloat(v, View.ALPHA, 0f, 1f);
+        alpha.setInterpolator(LINEAR);
+        alpha.setDuration(ALPHA_DURATION_MS);
+        alpha.setStartDelay(startDelay);
+        mAnimators.add(alpha);
+    }
+
+    private void addWorkspaceScrimAnimationForState(Launcher launcher, LauncherState state,
+            long duration) {
+        AnimatorSetBuilder scrimAnimBuilder = new AnimatorSetBuilder();
+        AnimationConfig scrimAnimConfig = new AnimationConfig();
+        scrimAnimConfig.duration = duration;
+        PropertySetter scrimPropertySetter = scrimAnimConfig.getPropertySetter(scrimAnimBuilder);
+        launcher.getWorkspace().getStateTransitionAnimation().setScrim(scrimPropertySetter, state);
+        mAnimators.add(scrimAnimBuilder.build());
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java
new file mode 100644
index 0000000..381c27a
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java
@@ -0,0 +1,119 @@
+/*
+ * 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.quickstep.TouchInteractionService.BACKGROUND_EXECUTOR;
+import static com.android.quickstep.TouchInteractionService.MAIN_THREAD_EXECUTOR;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
+
+import android.graphics.Rect;
+
+import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+import java.util.function.Consumer;
+
+/**
+ * Extension of {@link RemoteAnimationTargetSet} with additional information about swipe
+ * up animation
+ */
+public class SwipeAnimationTargetSet extends RemoteAnimationTargetSet {
+
+    private final boolean mShouldMinimizeSplitScreen;
+    private final Consumer<SwipeAnimationTargetSet> mOnFinishListener;
+
+    public final RecentsAnimationControllerCompat controller;
+    public final Rect homeContentInsets;
+    public final Rect minimizedHomeBounds;
+
+    public SwipeAnimationTargetSet(RecentsAnimationControllerCompat controller,
+            RemoteAnimationTargetCompat[] targets, Rect homeContentInsets,
+            Rect minimizedHomeBounds, boolean shouldMinimizeSplitScreen,
+            Consumer<SwipeAnimationTargetSet> onFinishListener) {
+        super(targets, MODE_CLOSING);
+        this.controller = controller;
+        this.homeContentInsets = homeContentInsets;
+        this.minimizedHomeBounds = minimizedHomeBounds;
+        this.mShouldMinimizeSplitScreen = shouldMinimizeSplitScreen;
+        this.mOnFinishListener = onFinishListener;
+    }
+
+    public boolean hasTargets() {
+        return unfilteredApps.length != 0;
+    }
+
+    /**
+     * Clones the target set without any actual targets. Used only when continuing a gesture after
+     * the actual recents animation has finished.
+     */
+    public SwipeAnimationTargetSet cloneWithoutTargets() {
+        return new SwipeAnimationTargetSet(controller, new RemoteAnimationTargetCompat[0],
+                homeContentInsets, minimizedHomeBounds, mShouldMinimizeSplitScreen,
+                mOnFinishListener);
+    }
+
+    public void finishController(boolean toRecents, Runnable callback, boolean sendUserLeaveHint) {
+        mOnFinishListener.accept(this);
+        BACKGROUND_EXECUTOR.execute(() -> {
+            controller.setInputConsumerEnabled(false);
+            controller.finish(toRecents, sendUserLeaveHint);
+
+            if (callback != null) {
+                MAIN_THREAD_EXECUTOR.execute(callback);
+            }
+        });
+    }
+
+    public void enableInputConsumer() {
+        BACKGROUND_EXECUTOR.submit(() -> {
+            controller.hideCurrentInputMethod();
+            controller.setInputConsumerEnabled(true);
+        });
+    }
+
+    public void setWindowThresholdCrossed(boolean thresholdCrossed) {
+        BACKGROUND_EXECUTOR.execute(() -> {
+            controller.setAnimationTargetsBehindSystemBars(!thresholdCrossed);
+            if (mShouldMinimizeSplitScreen && thresholdCrossed) {
+                // NOTE: As a workaround for conflicting animations (Launcher animating the task
+                // leash, and SystemUI resizing the docked stack, which resizes the task), we
+                // currently only set the minimized mode, and not the inverse.
+                // TODO: Synchronize the minimize animation with the launcher animation
+                controller.setSplitScreenMinimized(thresholdCrossed);
+            }
+        });
+    }
+
+    public ThumbnailData screenshotTask(int taskId) {
+        return controller != null ? controller.screenshotTask(taskId) : null;
+    }
+
+    public void cancelAnimation() {
+        finishController(false /* toRecents */, null, false /* sendUserLeaveHint */);
+    }
+
+    public void finishAnimation() {
+        finishController(true /* toRecents */, null, false /* sendUserLeaveHint */);
+    }
+
+    public interface SwipeAnimationListener {
+
+        void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet);
+
+        void onRecentsAnimationCanceled();
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskCornerRadius.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskCornerRadius.java
new file mode 100644
index 0000000..3ddf1b6
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskCornerRadius.java
@@ -0,0 +1,32 @@
+/*
+ * 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.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows;
+
+import android.content.Context;
+
+import com.android.launcher3.R;
+import com.android.launcher3.util.Themes;
+
+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);
+    }
+}
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
new file mode 100644
index 0000000..9db0c09
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java
@@ -0,0 +1,101 @@
+/*
+ * 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.Property;
+import android.widget.Button;
+
+import com.android.launcher3.Utilities;
+import com.android.quickstep.views.RecentsView.PageCallbacks;
+import com.android.quickstep.views.RecentsView.ScrollState;
+
+public class ClearAllButton extends Button implements PageCallbacks {
+
+    public static final Property<ClearAllButton, Float> VISIBILITY_ALPHA =
+            new Property<ClearAllButton, Float>(Float.class, "visibilityAlpha") {
+                @Override
+                public Float get(ClearAllButton view) {
+                    return view.mVisibilityAlpha;
+                }
+
+                @Override
+                public void set(ClearAllButton view, Float visibilityAlpha) {
+                    view.setVisibilityAlpha(visibilityAlpha);
+                }
+            };
+
+    private float mScrollAlpha = 1;
+    private float mContentAlpha = 1;
+    private float mVisibilityAlpha = 1;
+
+    private final boolean mIsRtl;
+
+    private int mScrollOffset;
+
+    public ClearAllButton(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mIsRtl = Utilities.isRtl(context.getResources());
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+
+        RecentsView parent = (RecentsView) getParent();
+        mScrollOffset = mIsRtl ? parent.getPaddingRight() / 2 : - parent.getPaddingLeft() / 2;
+    }
+
+    @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) {
+        float width = getWidth();
+        if (width == 0) {
+            return;
+        }
+
+        float shift = Math.min(scrollState.scrollFromEdge, width);
+        setTranslationX(mIsRtl ? (mScrollOffset - shift) : (mScrollOffset + shift));
+        mScrollAlpha = 1 - shift / width;
+        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/DigitalWellBeingToast.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java
new file mode 100644
index 0000000..7fac813
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java
@@ -0,0 +1,237 @@
+/*
+ * 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 android.provider.Settings.ACTION_APP_USAGE_SETTINGS;
+
+import static com.android.launcher3.Utilities.prefixTextWithIcon;
+
+import android.annotation.TargetApi;
+import android.app.ActivityOptions;
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.content.pm.LauncherApps.AppUsageLimit;
+import android.icu.text.MeasureFormat;
+import android.icu.text.MeasureFormat.FormatWidth;
+import android.icu.util.Measure;
+import android.icu.util.MeasureUnit;
+import android.os.Build;
+import android.os.UserHandle;
+import android.util.Log;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.annotation.StringRes;
+
+import com.android.launcher3.BaseActivity;
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.systemui.shared.recents.model.Task;
+
+import java.time.Duration;
+import java.util.Locale;
+
+@TargetApi(Build.VERSION_CODES.Q)
+public final class DigitalWellBeingToast {
+    static final Intent OPEN_APP_USAGE_SETTINGS_TEMPLATE = new Intent(ACTION_APP_USAGE_SETTINGS);
+    static final int MINUTE_MS = 60000;
+
+    private static final String TAG = DigitalWellBeingToast.class.getSimpleName();
+
+    private final BaseDraggingActivity mActivity;
+    private final TaskView mTaskView;
+    private final LauncherApps mLauncherApps;
+
+    private Task mTask;
+    private boolean mHasLimit;
+    private long mAppRemainingTimeMs;
+
+    public DigitalWellBeingToast(BaseDraggingActivity activity, TaskView taskView) {
+        mActivity = activity;
+        mTaskView = taskView;
+        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);
+        mAppRemainingTimeMs = 0;
+    }
+
+    private void setLimit(long appUsageLimitTimeMs, long appRemainingTimeMs) {
+        mAppRemainingTimeMs = appRemainingTimeMs;
+        mHasLimit = true;
+        TextView toast = mActivity.getViewCache().getView(R.layout.digital_wellbeing_toast,
+                mActivity, mTaskView);
+        toast.setText(prefixTextWithIcon(mActivity, R.drawable.ic_hourglass_top, getText()));
+        toast.setOnClickListener(this::openAppUsageSettings);
+        setTaskFooter(toast);
+
+        mTaskView.setContentDescription(
+                getContentDescriptionForTask(mTask, appUsageLimitTimeMs, appRemainingTimeMs));
+        RecentsView rv = mTaskView.getRecentsView();
+        if (rv != null) {
+            rv.onDigitalWellbeingToastShown();
+        }
+    }
+
+    public String getText() {
+        return getText(mAppRemainingTimeMs);
+    }
+
+    public boolean hasLimit() {
+        return mHasLimit;
+    }
+
+    public void initialize(Task task) {
+        mTask = task;
+
+        if (task.key.userId != UserHandle.myUserId()) {
+            setNoLimit();
+            return;
+        }
+
+        Utilities.THREAD_POOL_EXECUTOR.execute(() -> {
+            final AppUsageLimit usageLimit = mLauncherApps.getAppUsageLimit(
+                    task.getTopComponent().getPackageName(),
+                    UserHandle.of(task.key.userId));
+
+            final long appUsageLimitTimeMs =
+                    usageLimit != null ? usageLimit.getTotalUsageLimit() : -1;
+            final long appRemainingTimeMs =
+                    usageLimit != null ? usageLimit.getUsageRemaining() : -1;
+
+            mTaskView.post(() -> {
+                if (appUsageLimitTimeMs < 0 || appRemainingTimeMs < 0) {
+                    setNoLimit();
+                } else {
+                    setLimit(appUsageLimitTimeMs, appRemainingTimeMs);
+                }
+            });
+        });
+    }
+
+    private String getReadableDuration(
+            Duration duration,
+            FormatWidth formatWidthHourAndMinute,
+            @StringRes int durationLessThanOneMinuteStringId,
+            boolean forceFormatWidth) {
+        int hours = Math.toIntExact(duration.toHours());
+        int minutes = Math.toIntExact(duration.minusHours(hours).toMinutes());
+
+        // Apply formatWidthHourAndMinute if both the hour part and the minute part are non-zero.
+        if (hours > 0 && minutes > 0) {
+            return MeasureFormat.getInstance(Locale.getDefault(), formatWidthHourAndMinute)
+                    .formatMeasures(
+                            new Measure(hours, MeasureUnit.HOUR),
+                            new Measure(minutes, MeasureUnit.MINUTE));
+        }
+
+        // Apply formatWidthHourOrMinute if only the hour part is non-zero (unless forced).
+        if (hours > 0) {
+            return MeasureFormat.getInstance(
+                    Locale.getDefault(),
+                    forceFormatWidth ? formatWidthHourAndMinute : FormatWidth.WIDE)
+                    .formatMeasures(new Measure(hours, MeasureUnit.HOUR));
+        }
+
+        // Apply formatWidthHourOrMinute if only the minute part is non-zero (unless forced).
+        if (minutes > 0) {
+            return MeasureFormat.getInstance(
+                    Locale.getDefault()
+                    , forceFormatWidth ? formatWidthHourAndMinute : FormatWidth.WIDE)
+                    .formatMeasures(new Measure(minutes, MeasureUnit.MINUTE));
+        }
+
+        // Use a specific string for usage less than one minute but non-zero.
+        if (duration.compareTo(Duration.ZERO) > 0) {
+            return mActivity.getString(durationLessThanOneMinuteStringId);
+        }
+
+        // Otherwise, return 0-minute string.
+        return MeasureFormat.getInstance(
+                Locale.getDefault(), forceFormatWidth ? formatWidthHourAndMinute : FormatWidth.WIDE)
+                .formatMeasures(new Measure(0, MeasureUnit.MINUTE));
+    }
+
+    private String getReadableDuration(
+            Duration duration,
+            FormatWidth formatWidthHourAndMinute,
+            @StringRes int durationLessThanOneMinuteStringId) {
+        return getReadableDuration(
+                duration,
+                formatWidthHourAndMinute,
+                durationLessThanOneMinuteStringId,
+                /* forceFormatWidth= */ false);
+    }
+
+    private String getRoundedUpToMinuteReadableDuration(long remainingTime) {
+        final Duration duration = Duration.ofMillis(
+                remainingTime > MINUTE_MS ?
+                        (remainingTime + MINUTE_MS - 1) / MINUTE_MS * MINUTE_MS :
+                        remainingTime);
+        return getReadableDuration(
+                duration, FormatWidth.NARROW, R.string.shorter_duration_less_than_one_minute);
+    }
+
+    private String getText(long remainingTime) {
+        return mActivity.getString(
+                R.string.time_left_for_app,
+                getRoundedUpToMinuteReadableDuration(remainingTime));
+    }
+
+    public void openAppUsageSettings(View view) {
+        final Intent intent = new Intent(OPEN_APP_USAGE_SETTINGS_TEMPLATE)
+                .putExtra(Intent.EXTRA_PACKAGE_NAME,
+                        mTask.getTopComponent().getPackageName()).addFlags(
+                        Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        try {
+            final BaseActivity activity = BaseActivity.fromContext(view.getContext());
+            final ActivityOptions options = ActivityOptions.makeScaleUpAnimation(
+                    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);
+        } catch (ActivityNotFoundException e) {
+            Log.e(TAG, "Failed to open app usage settings for task "
+                    + mTask.getTopComponent().getPackageName(), e);
+        }
+    }
+
+    private String getContentDescriptionForTask(
+            Task task, long appUsageLimitTimeMs, long appRemainingTimeMs) {
+        return appUsageLimitTimeMs >= 0 && appRemainingTimeMs >= 0 ?
+                mActivity.getString(
+                        R.string.task_contents_description_with_remaining_time,
+                        task.titleDescription,
+                        getText(appRemainingTimeMs)) :
+                task.titleDescription;
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/IconView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/IconView.java
new file mode 100644
index 0000000..eb8da6e
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/IconView.java
@@ -0,0 +1,133 @@
+/*
+ * 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.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.android.launcher3.FastBitmapDrawable;
+
+import java.util.ArrayList;
+
+import androidx.annotation.NonNull;
+
+/**
+ * A view which draws a drawable stretched to fit its size. Unlike ImageView, it avoids relayout
+ * when the drawable changes.
+ */
+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);
+    }
+
+    public IconView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public IconView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public void setDrawable(Drawable d) {
+        if (mDrawable != null) {
+            mDrawable.setCallback(null);
+        }
+        mDrawable = d;
+        if (mDrawable != null) {
+            mDrawable.setCallback(this);
+            mDrawable.setBounds(0, 0, getWidth(), getHeight());
+        }
+        invalidate();
+    }
+
+    public Drawable getDrawable() {
+        return mDrawable;
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+        if (mDrawable != null) {
+            mDrawable.setBounds(0, 0, w, h);
+        }
+    }
+
+    @Override
+    protected boolean verifyDrawable(Drawable who) {
+        return super.verifyDrawable(who) || who == mDrawable;
+    }
+
+    @Override
+    protected void drawableStateChanged() {
+        super.drawableStateChanged();
+
+        final Drawable drawable = mDrawable;
+        if (drawable != null && drawable.isStateful()
+                && drawable.setState(getDrawableState())) {
+            invalidateDrawable(drawable);
+        }
+    }
+
+    @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);
+        }
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        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);
+        }
+    }
+}
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
new file mode 100644
index 0000000..03441c8
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -0,0 +1,267 @@
+/*
+ * 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.RECENTS_CLEAR_ALL_BUTTON;
+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.graphics.Canvas;
+import android.graphics.Rect;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Hotseat;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager.StateListener;
+import com.android.launcher3.R;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.appprediction.PredictionUiStateManager;
+import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
+import com.android.launcher3.views.ScrimView;
+import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.util.ClipAnimationHelper;
+import com.android.quickstep.util.ClipAnimationHelper.TransformParams;
+import com.android.quickstep.util.LayoutUtils;
+
+/**
+ * {@link RecentsView} used in Launcher activity
+ */
+@TargetApi(Build.VERSION_CODES.O)
+public class LauncherRecentsView extends RecentsView<Launcher> implements StateListener {
+
+    private final TransformParams mTransformParams = new TransformParams();
+
+    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);
+        setContentAlpha(0);
+        mActivity.getStateManager().addStateListener(this);
+    }
+
+    @Override
+    public void startHome() {
+        mActivity.getStateManager().goToState(NORMAL);
+    }
+
+    @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);
+            }
+        }
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        maybeDrawEmptyMessage(canvas);
+        super.draw(canvas);
+    }
+
+    @Override
+    public void onViewAdded(View child) {
+        super.onViewAdded(child);
+        updateEmptyMessage();
+    }
+
+    @Override
+    protected void onTaskStackUpdated() {
+        // Lazily update the empty message only when the task stack is reapplied
+        updateEmptyMessage();
+    }
+
+    /**
+     * Animates adjacent tasks and translate hotseat off screen as well.
+     */
+    @Override
+    public AnimatorSet createAdjacentPageAnimForTaskLaunch(TaskView tv,
+            ClipAnimationHelper helper) {
+        AnimatorSet anim = super.createAdjacentPageAnimForTaskLaunch(tv, helper);
+
+        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.findViewById(R.id.scrim_view), ScrimView.DRAG_HANDLE_ALPHA, 0);
+        dragHandleAnim.setInterpolator(Interpolators.ACCEL_2);
+        anim.play(dragHandleAnim);
+
+        return anim;
+    }
+
+    @Override
+    protected void getTaskSize(DeviceProfile dp, Rect outRect) {
+        LayoutUtils.calculateLauncherTaskSize(getContext(), dp, outRect);
+    }
+
+    @Override
+    protected void onTaskLaunchAnimationUpdate(float progress, TaskView tv) {
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+            if (mRecentsAnimationWrapper.targetSet != null && tv.isRunningTask()) {
+                mTransformParams.setProgress(1 - progress)
+                        .setSyncTransactionApplier(mSyncTransactionApplier)
+                        .setForLiveTile(true);
+                mClipAnimationHelper.applyTransform(mRecentsAnimationWrapper.targetSet,
+                        mTransformParams);
+            } else {
+                redrawLiveTile(true);
+            }
+        }
+    }
+
+    @Override
+    protected void onTaskLaunched(boolean success) {
+        if (success) {
+            mActivity.getStateManager().goToState(NORMAL, false /* animate */);
+        } else {
+            LauncherState state = mActivity.getStateManager().getState();
+            mActivity.getAllAppsController().setState(state);
+        }
+        super.onTaskLaunched(success);
+    }
+
+    @Override
+    public boolean shouldUseMultiWindowTaskSizeStrategy() {
+        return 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 void redrawLiveTile(boolean mightNeedToRefill) {
+        if (!mEnableDrawingLiveTile || mRecentsAnimationWrapper == null
+                || mClipAnimationHelper == null) {
+            return;
+        }
+        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;
+            }
+            mTempRectF.set(mTempRect);
+            mTransformParams.setProgress(1f)
+                    .setCurrentRectAndTargetAlpha(mTempRectF, taskView.getAlpha())
+                    .setSyncTransactionApplier(mSyncTransactionApplier);
+            if (mRecentsAnimationWrapper.targetSet != null) {
+                mClipAnimationHelper.applyTransform(mRecentsAnimationWrapper.targetSet,
+                        mTransformParams);
+            }
+        }
+    }
+
+    @Override
+    public void reset() {
+        super.reset();
+
+        // 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);
+        setFreezeViewVisibility(false);
+    }
+
+    @Override
+    public void setOverviewStateEnabled(boolean enabled) {
+        super.setOverviewStateEnabled(enabled);
+        if (enabled) {
+            LauncherState state = mActivity.getStateManager().getState();
+            boolean hasClearAllButton = (state.getVisibleElements(mActivity)
+                    & RECENTS_CLEAR_ALL_BUTTON) != 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);
+    }
+}
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
new file mode 100644
index 0000000..a838797
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LiveTileOverlay.java
@@ -0,0 +1,131 @@
+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 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;
+                }
+            };
+
+    private final Paint mPaint = new Paint();
+
+    private Rect mBoundsRect = new Rect();
+    private RectF mCurrentRect;
+    private float mCornerRadius;
+    private Drawable mIcon;
+    private Animator mIconAnimator;
+
+    private boolean mDrawEnabled = true;
+    private float mIconAnimationProgress = 0f;
+
+    public 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;
+    }
+
+    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
new file mode 100644
index 0000000..a98df0f
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -0,0 +1,1736 @@
+/*
+ * 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 androidx.dynamicanimation.animation.DynamicAnimation.MIN_VISIBLE_CHANGE_PIXELS;
+
+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.SCALE_PROPERTY;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
+import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
+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_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.config.FeatureFlags.QUICKSTEP_SPRINGS;
+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.SystemUiController.UI_STATE_OVERVIEW;
+import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.LayoutTransition;
+import android.animation.LayoutTransition.TransitionListener;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+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.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Point;
+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.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.widget.ListView;
+
+import androidx.annotation.Nullable;
+import androidx.dynamicanimation.animation.SpringForce;
+
+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.AnimatorPlaybackController;
+import com.android.launcher3.anim.PropertyListBuilder;
+import com.android.launcher3.anim.SpringObjectAnimator;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.graphics.RotationMode;
+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.OverScroller;
+import com.android.launcher3.util.PendingAnimation;
+import com.android.launcher3.util.Themes;
+import com.android.launcher3.util.ViewPool;
+import com.android.quickstep.RecentsAnimationWrapper;
+import com.android.quickstep.RecentsModel;
+import com.android.quickstep.RecentsModel.TaskThumbnailChangeListener;
+import com.android.quickstep.TaskThumbnailCache;
+import com.android.quickstep.TaskUtils;
+import com.android.quickstep.util.ClipAnimationHelper;
+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.BackgroundExecutor;
+import com.android.systemui.shared.system.LauncherEventUtil;
+import com.android.systemui.shared.system.PackageManagerWrapper;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
+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.P)
+public abstract class RecentsView<T extends BaseActivity> extends PagedView implements Insettable,
+        TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
+        InvariantDeviceProfile.OnIDPChangeListener, TaskThumbnailChangeListener {
+
+    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;
+                }
+            };
+
+    protected RecentsAnimationWrapper mRecentsAnimationWrapper;
+    protected ClipAnimationHelper mClipAnimationHelper;
+    protected SyncRtSurfaceTransactionApplierCompat mSyncTransactionApplier;
+    protected int mTaskWidth;
+    protected int mTaskHeight;
+    protected boolean mEnableDrawingLiveTile = false;
+    protected final Rect mTempRect = new Rect();
+    protected final RectF mTempRectF = new RectF();
+
+    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();
+    protected final ClipAnimationHelper mTempClipAnimationHelper;
+
+    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 boolean mDwbToastShown;
+    private boolean mDisallowScrollToClearAll;
+    private boolean mOverlayEnabled;
+    private boolean mFreezeViewVisibility;
+
+    /**
+     * 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;
+            }
+
+            BackgroundExecutor.get().submit(() -> {
+                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 */));
+                        }
+                    });
+                }
+            });
+        }
+
+        @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);
+        }
+    };
+
+    // 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
+    private int mRunningTaskId = -1;
+    private 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")
+    private float mContentAlpha = 1;
+    @ViewDebug.ExportedProperty(category = "launcher")
+    private float mFullscreenProgress = 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 Layout mEmptyTextLayout;
+    private LiveTileOverlay mLiveTileOverlay;
+
+    private BaseActivity.MultiWindowModeChangedListener mMultiWindowModeChangedListener =
+            (inMultiWindowMode) -> {
+        if (!inMultiWindowMode && mOverviewStateEnabled) {
+            // TODO: Re-enable layout transitions for addition of the unpinned task
+            reloadIfNeeded();
+        }
+    };
+
+    public RecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        setPageSpacing(getResources().getDimensionPixelSize(R.dimen.recents_page_spacing));
+        setEnableFreeScroll(true);
+
+        mFastFlingVelocity = getResources()
+                .getDimensionPixelSize(R.dimen.recents_fast_fling_velocity);
+        mActivity = (T) BaseActivity.fromContext(context);
+        mModel = RecentsModel.INSTANCE.get(context);
+        mIdp = InvariantDeviceProfile.INSTANCE.get(context);
+        mTempClipAnimationHelper = new ClipAnimationHelper(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 = !Utilities.isRtl(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();
+
+        // 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;
+    }
+
+    public TaskView updateThumbnail(int taskId, ThumbnailData thumbnailData) {
+        TaskView taskView = getTaskView(taskId);
+        if (taskView != null) {
+            taskView.getThumbnail().setThumbnail(taskView.getTask(), thumbnailData);
+        }
+        return taskView;
+    }
+
+    @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();
+        reset();
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        updateTaskStackListenerState();
+        mModel.getThumbnailCache().getHighResLoadingState().addCallback(this);
+        mActivity.addMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
+        ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
+        mSyncTransactionApplier = new SyncRtSurfaceTransactionApplierCompat(this);
+        RecentsModel.INSTANCE.get(getContext()).addThumbnailChangeListener(this);
+        mIdp.addOnChangeListener(this);
+    }
+
+    @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);
+    }
+
+    @Override
+    public void onViewRemoved(View child) {
+        super.onViewRemoved(child);
+
+        // Clear the task data for the removed child if it was visible
+        if (child != mClearAllButton) {
+            TaskView taskView = (TaskView) child;
+            mHasVisibleTaskData.delete(taskView.getTask().key.id);
+            mTaskViewPool.recycle(taskView);
+        }
+    }
+
+    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 = (TaskView) getChildAt(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();
+    }
+
+    public void onDigitalWellbeingToastShown() {
+        if (!mDwbToastShown) {
+            mDwbToastShown = true;
+            mActivity.getUserEventDispatcher().logActionTip(
+                    LauncherEventUtil.VISIBLE,
+                    LauncherLogProto.TipType.DWB_TOAST);
+        }
+    }
+
+    @Override
+    protected void onPageEndTransition() {
+        super.onPageEndTransition();
+        if (getNextPage() > 0) {
+            setSwipeDownShouldLaunchApp(true);
+        }
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        super.onTouchEvent(ev);
+        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()) {
+                    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);
+    }
+
+    protected boolean shouldStealTouchFromSiblingsBelow(MotionEvent ev) {
+        return true;
+    }
+
+    private void applyLoadPlan(ArrayList<Task> tasks) {
+        if (mPendingAnimation != null) {
+            mPendingAnimation.addEndListener((onEndListener) -> applyLoadPlan(tasks));
+            return;
+        }
+
+        if (tasks == null || tasks.isEmpty()) {
+            removeAllViews();
+            onTaskStackUpdated();
+            return;
+        }
+
+        int oldChildCount = getChildCount();
+
+        // Unload existing visible task data
+        unloadVisibleTaskData();
+
+        TaskView ignoreRestTaskView =
+                mIgnoreResetTaskId == -1 ? null : getTaskView(mIgnoreResetTaskId);
+
+        final int requiredTaskCount = tasks.size();
+        if (getTaskViewCount() != requiredTaskCount) {
+            if (oldChildCount > 0) {
+                removeView(mClearAllButton);
+            }
+            for (int i = getChildCount(); i < requiredTaskCount; i++) {
+                addView(mTaskViewPool.getView());
+            }
+            while (getChildCount() > 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;
+            final Task task = tasks.get(i);
+            final TaskView taskView = (TaskView) getChildAt(pageIndex);
+            taskView.bind(task);
+        }
+        TaskView runningTaskView = getRunningTaskView();
+        if (runningTaskView != null) {
+            setCurrentPage(indexOfChild(runningTaskView));
+        }
+
+        if (mIgnoreResetTaskId != -1 && getTaskView(mIgnoreResetTaskId) != ignoreRestTaskView) {
+            // 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();
+    }
+
+    public int getTaskViewCount() {
+        // Account for the clear all button.
+        int childCount = getChildCount();
+        return childCount == 0 ? 0 : childCount - 1;
+    }
+
+    protected void onTaskStackUpdated() { }
+
+    public void resetTaskVisuals() {
+        for (int i = getTaskViewCount() - 1; i >= 0; i--) {
+            TaskView taskView = (TaskView) getChildAt(i);
+            if (mIgnoreResetTaskId != taskView.getTask().key.id) {
+                taskView.resetVisualProperties();
+            }
+        }
+        if (mRunningTaskTileHidden) {
+            setRunningTaskHidden(mRunningTaskTileHidden);
+        }
+
+        // Force apply the scale.
+        if (mIgnoreResetTaskId != mRunningTaskId) {
+            applyRunningTaskIconScale();
+        }
+
+        updateCurveProperties();
+        // Update the set of visible task's data
+        loadVisibleTaskData();
+    }
+
+    public void setFullscreenProgress(float fullscreenProgress) {
+        mFullscreenProgress = fullscreenProgress;
+        int taskCount = getTaskViewCount();
+        for (int i = 0; i < taskCount; i++) {
+            getTaskViewAt(i).setFullscreenProgress(mFullscreenProgress);
+        }
+    }
+
+    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);
+        DeviceProfile dp = mActivity.getDeviceProfile();
+        getTaskSize(dp, 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);
+    }
+
+    protected abstract void getTaskSize(DeviceProfile dp, Rect outRect);
+
+    public void getTaskSize(Rect outRect) {
+        getTaskSize(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;
+        }
+        int scrollX = getScrollX();
+        final int halfPageWidth = getNormalChildWidth() / 2;
+        final int screenCenter = mInsets.left + getPaddingLeft() + scrollX + halfPageWidth;
+        final int halfScreenWidth = getMeasuredWidth() / 2;
+        final int pageSpacing = mPageSpacing;
+        mScrollState.scrollFromEdge = mIsRtl ? scrollX : (mMaxScrollX - scrollX);
+
+        final int pageCount = getPageCount();
+        for (int i = 0; i < pageCount; i++) {
+            View page = getPageAt(i);
+            float pageCenter = page.getLeft() + page.getTranslationX() + halfPageWidth;
+            float distanceFromScreenCenter = screenCenter - pageCenter;
+            float distanceToReachEdge = halfScreenWidth + halfPageWidth + pageSpacing;
+            mScrollState.linearInterpolation = Math.min(1,
+                    Math.abs(distanceFromScreenCenter) / distanceToReachEdge);
+            ((PageCallbacks) page).onPageScroll(mScrollState);
+        }
+    }
+
+    /**
+     * Iterates through all thet asks, 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 = getTaskViewCount();
+        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 < numChildren; i++) {
+            TaskView taskView = (TaskView) getChildAt(i);
+            Task task = taskView.getTask();
+            boolean visible = lower <= i && i <= 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();
+
+    public void reset() {
+        setCurrentTask(-1);
+        mIgnoreResetTaskId = -1;
+        mTaskListChangeId = -1;
+
+        mRecentsAnimationWrapper = null;
+        mClipAnimationHelper = null;
+
+        unloadVisibleTaskData();
+        setCurrentPage(0);
+        mDwbToastShown = false;
+    }
+
+    public @Nullable TaskView getRunningTaskView() {
+        return getTaskView(mRunningTaskId);
+    }
+
+    public int getRunningTaskIndex() {
+        TaskView tv = getRunningTaskView();
+        return tv == null ? -1 : indexOfChild(tv);
+    }
+
+    /**
+     * 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(int runningTaskId) {
+        // This needs to be called before the other states are set since it can create the task view
+        showCurrentTask(runningTaskId);
+        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) {
+            float startProgress = ENABLE_QUICKSTEP_LIVE_TILE.get()
+                    ? mLiveTileOverlay.cancelIconAnimation()
+                    : 0f;
+            animateUpRunningTaskIconScale(startProgress);
+        }
+        setSwipeDownShouldLaunchApp(true);
+    }
+
+    /**
+     * Called when a gesture from an app has finished.
+     */
+    public void onGestureAnimationEnd() {
+        setEnableFreeScroll(true);
+        setEnableDrawingLiveTile(true);
+        setOnScrollChangeListener(null);
+        setRunningTaskViewShowScreenshot(true);
+        setRunningTaskHidden(false);
+        animateUpRunningTaskIconScale();
+    }
+
+    /**
+     * 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(int runningTaskId) {
+        if (getChildCount() == 0) {
+            // Add an empty view for now until the task plan is loaded and applied
+            final TaskView taskView = mTaskViewPool.getView();
+            addView(taskView);
+            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 = new Task(new Task.TaskKey(runningTaskId, 0, new Intent(),
+                    new ComponentName(getContext(), getClass()), 0, 0), null, null, "", "", 0, 0,
+                    false, true, false, false, new ActivityManager.TaskDescription(), 0,
+                    new ComponentName("", ""), false);
+            taskView.bind(mTmpRunningTask);
+        }
+
+        boolean runningTaskTileHidden = mRunningTaskTileHidden;
+        setCurrentTask(runningTaskId);
+        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);
+        }
+    }
+
+    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 /* animate */);
+            }
+        } else {
+            TaskView nextTaskView = getNextTaskView();
+            if (nextTaskView != null) {
+                nextTaskView.launchTask(true /* animate */);
+            } else {
+                runningTaskView.launchTask(true /* animate */);
+            }
+        }
+    }
+
+    public void setRunningTaskIconScaledDown(boolean isScaledDown) {
+        if (mRunningTaskIconScaledDown != isScaledDown) {
+            mRunningTaskIconScaledDown = isScaledDown;
+            applyRunningTaskIconScale();
+        }
+    }
+
+    private void applyRunningTaskIconScale() {
+        TaskView firstTask = getRunningTaskView();
+        if (firstTask != null) {
+            firstTask.setIconScaleAndDim(mRunningTaskIconScaledDown ? 0 : 1);
+        }
+    }
+
+    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 {
+
+        /**
+         * 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;
+    }
+
+    public void setIgnoreResetTask(int taskId) {
+        mIgnoreResetTaskId = taskId;
+    }
+
+    public void clearIgnoreResetTask(int taskId) {
+        if (mIgnoreResetTaskId == taskId) {
+            mIgnoreResetTaskId = -1;
+        }
+    }
+
+    private void addDismissedTaskAnimations(View taskView, AnimatorSet anim, long duration) {
+        addAnim(ObjectAnimator.ofFloat(taskView, ALPHA, 0), duration, ACCEL_2, anim);
+        if (QUICKSTEP_SPRINGS.get() && taskView instanceof TaskView)
+            addAnim(new SpringObjectAnimator<>(taskView, VIEW_TRANSLATE_Y,
+                            MIN_VISIBLE_CHANGE_PIXELS, SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY,
+                            SpringForce.STIFFNESS_MEDIUM,
+                            0, -taskView.getHeight()),
+                    duration, LINEAR, anim);
+        else {
+            addAnim(ObjectAnimator.ofFloat(taskView, TRANSLATION_Y, -taskView.getHeight()),
+                    duration, LINEAR, anim);
+        }
+    }
+
+    private void removeTask(Task task, int index, PendingAnimation.OnEndListener onEndListener,
+                            boolean shouldLog) {
+        if (task != null) {
+            ActivityManagerWrapper.getInstance().removeTask(task.key.id);
+            if (shouldLog) {
+                mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
+                        onEndListener.logAction, Direction.UP, index,
+                        TaskUtils.getLaunchComponentKeyForTask(task.key));
+            }
+        }
+    }
+
+    public PendingAnimation createTaskDismissAnimation(TaskView taskView, boolean animateTaskView,
+            boolean shouldRemoveTask, long duration) {
+        if (mPendingAnimation != null) {
+            mPendingAnimation.finish(false, Touch.SWIPE);
+        }
+        AnimatorSet anim = new AnimatorSet();
+        PendingAnimation pendingAnimation = new PendingAnimation(anim);
+
+        int count = getPageCount();
+        if (count == 0) {
+            return pendingAnimation;
+        }
+
+        int[] oldScroll = new int[count];
+        getPageScrolls(oldScroll, false, SIMPLE_SCROLL_LOGIC);
+
+        int[] newScroll = new int[count];
+        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, anim, duration);
+                }
+            } 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) {
+                    if (QUICKSTEP_SPRINGS.get() && child instanceof TaskView) {
+                        addAnim(new SpringObjectAnimator<>(child, VIEW_TRANSLATE_X,
+                                MIN_VISIBLE_CHANGE_PIXELS, SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY,
+                                SpringForce.STIFFNESS_MEDIUM,
+                                0, scrollDiff), duration, ACCEL, anim);
+                    } else {
+                        addAnim(ObjectAnimator.ofFloat(child, TRANSLATION_X, scrollDiff), duration,
+                                ACCEL, anim);
+                    }
+
+                    needsCurveUpdates = true;
+                }
+            }
+        }
+
+        if (needsCurveUpdates) {
+            ValueAnimator va = ValueAnimator.ofFloat(0, 1);
+            va.addUpdateListener((a) -> updateCurveProperties());
+            anim.play(va);
+        }
+
+        // Add a tiny bit of translation Z, so that it draws on top of other views
+        if (animateTaskView) {
+            taskView.setTranslationZ(0.1f);
+        }
+
+        mPendingAnimation = pendingAnimation;
+        mPendingAnimation.addEndListener(new Consumer<PendingAnimation.OnEndListener>() {
+            @Override
+            public void accept(PendingAnimation.OnEndListener onEndListener) {
+                if (ENABLE_QUICKSTEP_LIVE_TILE.get() &&
+                        taskView.isRunningTask() && onEndListener.isSuccess) {
+                    finishRecentsAnimation(true /* toHome */, () -> onEnd(onEndListener));
+                } else {
+                    onEnd(onEndListener);
+                }
+            }
+
+            private void onEnd(PendingAnimation.OnEndListener onEndListener) {
+                if (onEndListener.isSuccess) {
+                    if (shouldRemoveTask) {
+                        removeTask(taskView.getTask(), draggedIndex, onEndListener, true);
+                    }
+
+                    int pageToSnapTo = mCurrentPage;
+                    if (draggedIndex < pageToSnapTo ||
+                            pageToSnapTo == (getTaskViewCount() - 1)) {
+                        pageToSnapTo -= 1;
+                    }
+                    removeView(taskView);
+
+                    if (getTaskViewCount() == 0) {
+                        removeView(mClearAllButton);
+                        startHome();
+                    } else {
+                        snapToPageImmediately(pageToSnapTo);
+                    }
+                }
+                resetTaskVisuals();
+                mPendingAnimation = null;
+            }
+        });
+        return pendingAnimation;
+    }
+
+    public PendingAnimation createAllTasksDismissAnimation(long duration) {
+        if (FeatureFlags.IS_DOGFOOD_BUILD && mPendingAnimation != null) {
+            throw new IllegalStateException("Another pending animation is still running");
+        }
+        AnimatorSet anim = new AnimatorSet();
+        PendingAnimation pendingAnimation = new PendingAnimation(anim);
+
+        int count = getTaskViewCount();
+        for (int i = 0; i < count; i++) {
+            addDismissedTaskAnimations(getChildAt(i), anim, duration);
+        }
+
+        mPendingAnimation = pendingAnimation;
+        mPendingAnimation.addEndListener((onEndListener) -> {
+            if (onEndListener.isSuccess) {
+                // Remove all the task views now
+                ActivityManagerWrapper.getInstance().removeAllRecentTasks();
+                removeAllViews();
+                startHome();
+            }
+            mPendingAnimation = null;
+        });
+        return pendingAnimation;
+    }
+
+    private static void addAnim(Animator anim, long duration,
+            TimeInterpolator interpolator, AnimatorSet set) {
+        anim.setDuration(duration).setInterpolator(interpolator);
+        set.play(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 = AnimatorPlaybackController.wrap(
+                pendingAnim.anim, DISMISS_TASK_DURATION);
+        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 = getTaskView(getNextPage());
+        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);
+
+        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 onViewAdded(View child) {
+        super.onViewAdded(child);
+        child.setAlpha(mContentAlpha);
+    }
+
+    /**
+     * @return The most recent task that is older than the currently running task. If there is
+     * currently no running task or there is no task older than it, then return null.
+     */
+    @Nullable
+    public TaskView getNextTaskView() {
+        TaskView runningTaskView = getRunningTaskView();
+        if (runningTaskView == null) {
+            return null;
+        }
+        return getTaskViewAt(indexOfChild(runningTaskView) + 1);
+    }
+
+    public TaskView getTaskViewAt(int index) {
+        View child = getChildAt(index);
+        return child == mClearAllButton ? null : (TaskView) child;
+    }
+
+    public void updateEmptyMessage() {
+        boolean isEmpty = getChildCount() == 0;
+        boolean hasSizeChanged = mLastMeasureSize.x != getWidth()
+                || mLastMeasureSize.y != getHeight();
+        if (isEmpty == mShowEmptyMessage && !hasSizeChanged) {
+            return;
+        }
+        setContentDescription(isEmpty ? mEmptyMessage : "");
+        mShowEmptyMessage = isEmpty;
+        updateEmptyStateUi(hasSizeChanged);
+        invalidate();
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        updateEmptyStateUi(changed);
+
+        // Set the pivot points to match the task preview center
+        setPivotY(((mInsets.top + getPaddingTop() + mTaskTopMargin)
+                + (getHeight() - mInsets.bottom - getPaddingBottom())) / 2);
+        setPivotX(((mInsets.left + getPaddingLeft())
+                + (getWidth() - mInsets.right - getPaddingRight())) / 2);
+    }
+
+    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, ClipAnimationHelper clipAnimationHelper) {
+        AnimatorSet anim = new AnimatorSet();
+
+        int taskIndex = indexOfChild(tv);
+        int centerTaskIndex = getCurrentPage();
+        boolean launchingCenterTask = taskIndex == centerTaskIndex;
+
+        LauncherState.ScaleAndTranslation toScaleAndTranslation = clipAnimationHelper
+                .getScaleAndTranslation();
+        float toScale = toScaleAndTranslation.scale;
+        float toTranslationY = toScaleAndTranslation.translationY;
+        if (launchingCenterTask) {
+            RecentsView recentsView = tv.getRecentsView();
+            anim.play(ObjectAnimator.ofFloat(recentsView, SCALE_PROPERTY, toScale));
+            anim.play(ObjectAnimator.ofFloat(recentsView, TRANSLATION_Y, toTranslationY));
+            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;
+    }
+
+    public PendingAnimation createTaskLauncherAnimation(TaskView tv, long duration) {
+        if (FeatureFlags.IS_DOGFOOD_BUILD && mPendingAnimation != null) {
+            throw new IllegalStateException("Another pending animation is still running");
+        }
+
+        int count = getChildCount();
+        if (count == 0) {
+            return new PendingAnimation(new AnimatorSet());
+        }
+
+        int targetSysUiFlags = tv.getThumbnail().getSysUiStatusNavFlags();
+        final boolean[] passedOverviewThreshold = new boolean[] {false};
+        ValueAnimator progressAnim = ValueAnimator.ofFloat(0, 1);
+        progressAnim.setInterpolator(LINEAR);
+        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);
+            }
+        });
+
+        ClipAnimationHelper clipAnimationHelper = new ClipAnimationHelper(mActivity);
+        clipAnimationHelper.fromTaskThumbnailView(tv.getThumbnail(), this);
+        clipAnimationHelper.prepareAnimation(mActivity.getDeviceProfile(), true /* isOpening */);
+        AnimatorSet anim = createAdjacentPageAnimForTaskLaunch(tv, clipAnimationHelper);
+        anim.play(progressAnim);
+        anim.setDuration(duration);
+
+        Consumer<Boolean> onTaskLaunchFinish = this::onTaskLaunched;
+
+        mPendingAnimation = new PendingAnimation(anim);
+        mPendingAnimation.addEndListener((onEndListener) -> {
+            if (onEndListener.isSuccess) {
+                Consumer<Boolean> onLaunchResult = (result) -> {
+                    onTaskLaunchFinish.accept(result);
+                    if (!result) {
+                        tv.notifyTaskLaunchFailed(TAG);
+                    }
+                };
+                tv.launchTask(false, onLaunchResult, getHandler());
+                Task task = tv.getTask();
+                if (task != null) {
+                    mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
+                            onEndListener.logAction, Direction.DOWN, indexOfChild(tv),
+                            TaskUtils.getLaunchComponentKeyForTask(task.key));
+                }
+            } else {
+                onTaskLaunchFinish.accept(false);
+            }
+            mPendingAnimation = null;
+        });
+        return mPendingAnimation;
+    }
+
+    protected void onTaskLaunchAnimationUpdate(float progress, TaskView tv) {
+    }
+
+    public abstract boolean shouldUseMultiWindowTaskSizeStrategy();
+
+    protected void onTaskLaunched(boolean success) {
+        if (success) {
+            resetTaskVisuals();
+        }
+    }
+
+    @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] - 1);
+            event.setToIndex(taskViewCount - visibleTasks[0] - 1);
+            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) { }
+
+    public void setRecentsAnimationWrapper(RecentsAnimationWrapper recentsAnimationWrapper) {
+        mRecentsAnimationWrapper = recentsAnimationWrapper;
+    }
+
+    public void setClipAnimationHelper(ClipAnimationHelper clipAnimationHelper) {
+        mClipAnimationHelper = clipAnimationHelper;
+    }
+
+    public void setLiveTileOverlay(LiveTileOverlay liveTileOverlay) {
+        mLiveTileOverlay = liveTileOverlay;
+    }
+
+    public void updateLiveTileIcon(Drawable icon) {
+        if (mLiveTileOverlay != null) {
+            mLiveTileOverlay.setIcon(icon);
+        }
+    }
+
+    public void finishRecentsAnimation(boolean toRecents, Runnable onFinishComplete) {
+        if (mRecentsAnimationWrapper == null) {
+            if (onFinishComplete != null) {
+                onFinishComplete.run();
+            }
+            return;
+        }
+
+        mRecentsAnimationWrapper.finish(toRecents, onFinishComplete);
+    }
+
+    public void setDisallowScrollToClearAll(boolean disallowScrollToClearAll) {
+        if (mDisallowScrollToClearAll != disallowScrollToClearAll) {
+            mDisallowScrollToClearAll = disallowScrollToClearAll;
+            updateMinAndMaxScrollX();
+        }
+    }
+
+    @Override
+    protected int computeMinScrollX() {
+        if (mIsRtl && mDisallowScrollToClearAll) {
+            // We aren't showing the clear all button, so use the leftmost task as the min scroll.
+            return getScrollForPage(getTaskViewCount() - 1);
+        }
+        return super.computeMinScrollX();
+    }
+
+    @Override
+    protected int computeMaxScrollX() {
+        if (!mIsRtl && mDisallowScrollToClearAll) {
+            // We aren't showing the clear all button, so use the rightmost task as the max scroll.
+            return getScrollForPage(getTaskViewCount() - 1);
+        }
+        return super.computeMaxScrollX();
+    }
+
+    public ClearAllButton getClearAllButton() {
+        return mClearAllButton;
+    }
+
+    /**
+     * @return How many pixels the running task is offset on the x-axis due to the current scrollX.
+     */
+    public float getScrollOffset() {
+        int startScroll = getScrollForPage(getRunningTaskIndex());
+        int offsetX = startScroll - getScrollX();
+        offsetX *= getScaleX();
+        return offsetX;
+    }
+
+    public Consumer<MotionEvent> getEventDispatcher(RotationMode rotationMode) {
+        if (rotationMode.isTransposed) {
+            Matrix transform = new Matrix();
+            transform.setRotate(-rotationMode.surfaceRotation);
+
+            if (getWidth() > 0 && getHeight() > 0) {
+                float scale = ((float) getWidth()) / getHeight();
+                transform.postScale(scale, 1 / scale);
+            }
+
+            Matrix inverse = new Matrix();
+            transform.invert(inverse);
+            return e -> {
+                e.transform(transform);
+                super.onTouchEvent(e);
+                e.transform(inverse);
+            };
+        } else {
+            return super::onTouchEvent;
+        }
+    }
+
+    public ClipAnimationHelper getTempClipAnimationHelper() {
+        return mTempClipAnimationHelper;
+    }
+
+    private void updateEnabledOverlays() {
+        int overlayEnabledPage = mOverlayEnabled ? getNextPage() : -1;
+        int taskCount = getTaskViewCount();
+        for (int i = 0; i < taskCount; i++) {
+            ((TaskView) getChildAt(i)).setOverlayEnabled(i == overlayEnabledPage);
+        }
+    }
+
+    public void setOverlayEnabled(boolean overlayEnabled) {
+        if (mOverlayEnabled != overlayEnabled) {
+            mOverlayEnabled = overlayEnabled;
+            updateEnabledOverlays();
+        }
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java
new file mode 100644
index 0000000..c1f6b82
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java
@@ -0,0 +1,290 @@
+/*
+ * 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.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.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+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.util.Themes;
+import com.android.launcher3.views.BaseDragLayer;
+import com.android.quickstep.TaskOverlayFactory;
+import com.android.quickstep.TaskSystemShortcut;
+import com.android.quickstep.TaskUtils;
+import com.android.quickstep.views.IconView.OnScaleUpdateListener;
+
+import java.util.List;
+
+/**
+ * Contains options for a recent task when long-pressing its icon.
+ */
+public class TaskMenuView extends AbstractFloatingView {
+
+    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 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);
+    }
+
+    public TaskMenuView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        mActivity = BaseDraggingActivity.fromContext(context);
+        mThumbnailTopMargin = getResources().getDimension(R.dimen.task_thumbnail_top_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);
+    }
+
+    @Override
+    public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+            BaseDragLayer dl = mActivity.getDragLayer();
+            if (!dl.isEventOverView(this, ev)) {
+                // TODO: log this once we have a new container type for it?
+                close(true);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    protected void handleClose(boolean animate) {
+        if (animate) {
+            animateClose();
+        } else {
+            closeComplete();
+        }
+    }
+
+    @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) {
+        setX(x);
+        setY(y + mThumbnailTopMargin);
+    }
+
+    public static TaskMenuView 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;
+    }
+
+    private boolean populateAndShowForTask(TaskView taskView) {
+        if (isAttachedToWindow()) {
+            return false;
+        }
+        mActivity.getDragLayer().addView(this);
+        mTaskView = taskView;
+        addMenuOptions(mTaskView);
+        orientAroundTaskView(mTaskView);
+        post(this::animateOpen);
+        return true;
+    }
+
+    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);
+
+        final BaseDraggingActivity activity = BaseDraggingActivity.fromContext(getContext());
+        final List<TaskSystemShortcut> shortcuts =
+                TaskOverlayFactory.INSTANCE.get(getContext()).getEnabledShortcuts(taskView);
+        final int count = shortcuts.size();
+        for (int i = 0; i < count; ++i) {
+            final TaskSystemShortcut menuOption = shortcuts.get(i);
+            addMenuOption(menuOption, menuOption.getOnClickListener(activity, taskView));
+        }
+    }
+
+    private void addMenuOption(TaskSystemShortcut menuOption, OnClickListener onClickListener) {
+        ViewGroup menuOptionView = (ViewGroup) mActivity.getLayoutInflater().inflate(
+                R.layout.task_view_menu_option, this, false);
+        menuOption.setIconAndLabelFor(
+                menuOptionView.findViewById(R.id.icon), menuOptionView.findViewById(R.id.text));
+        menuOptionView.setOnClickListener(onClickListener);
+        mOptionLayout.addView(menuOptionView);
+    }
+
+    private void orientAroundTaskView(TaskView taskView) {
+        measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+        mActivity.getDragLayer().getDescendantRectRelativeToSelf(taskView, sTempRect);
+        Rect insets = mActivity.getDragLayer().getInsets();
+        BaseDragLayer.LayoutParams params = (BaseDragLayer.LayoutParams) getLayoutParams();
+        params.width = taskView.getMeasuredWidth();
+        params.gravity = Gravity.START;
+        setLayoutParams(params);
+        setScaleX(taskView.getScaleX());
+        setScaleY(taskView.getScaleY());
+        setPosition(sTempRect.left - insets.left, sTempRect.top - insets.top);
+    }
+
+    private void animateOpen() {
+        animateOpenOrClosed(false);
+        mIsOpen = true;
+    }
+
+    private void animateClose() {
+        animateOpenOrClosed(true);
+    }
+
+    private void animateOpenOrClosed(boolean closing) {
+        if (mOpenCloseAnimator != null && mOpenCloseAnimator.isRunning()) {
+            mOpenCloseAnimator.end();
+        }
+        mOpenCloseAnimator = new AnimatorSet();
+
+        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.addListener(new AnimationSuccessListener() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                setVisibility(VISIBLE);
+            }
+
+            @Override
+            public void onAnimationSuccess(Animator animator) {
+                if (closing) {
+                    closeComplete();
+                }
+            }
+        });
+        mOpenCloseAnimator.play(ObjectAnimator.ofFloat(this, ALPHA, closing ? 0 : 1));
+        mOpenCloseAnimator.setDuration(closing ? REVEAL_CLOSE_DURATION: REVEAL_OPEN_DURATION);
+        mOpenCloseAnimator.start();
+    }
+
+    private void closeComplete() {
+        mIsOpen = false;
+        mActivity.getDragLayer().removeView(this);
+    }
+
+    private RoundedRectRevealOutlineProvider createOpenCloseOutlineProvider() {
+        float radius = Themes.getDialogCornerRadius(getContext());
+        Rect fromRect = new Rect(0, 0, getWidth(), 0);
+        Rect toRect = new Rect(0, 0, getWidth(), getHeight());
+        return new RoundedRectRevealOutlineProvider(radius, radius, fromRect, toRect);
+    }
+
+    public View findMenuItemByText(String text) {
+        for (int i = mOptionLayout.getChildCount() - 1; i >= 0; --i) {
+            final ViewGroup menuOptionView = (ViewGroup) mOptionLayout.getChildAt(i);
+            if (text.equals(menuOptionView.<TextView>findViewById(R.id.text).getText())) {
+                return menuOptionView;
+            }
+        }
+        return null;
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
new file mode 100644
index 0000000..d55a520
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -0,0 +1,413 @@
+/*
+ * 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 com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_FULLSCREEN;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.util.AttributeSet;
+import android.util.FloatProperty;
+import android.util.Property;
+import android.view.View;
+
+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.util.SystemUiController;
+import com.android.launcher3.util.Themes;
+import com.android.quickstep.TaskOverlayFactory;
+import com.android.quickstep.TaskOverlayFactory.TaskOverlay;
+import com.android.quickstep.util.TaskCornerRadius;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+
+/**
+ * A task in the Recents view.
+ */
+public class TaskThumbnailView extends View {
+
+    private final static ColorMatrix COLOR_MATRIX = new ColorMatrix();
+    private final static ColorMatrix SATURATION_COLOR_MATRIX = new ColorMatrix();
+    private final static RectF EMPTY_RECT_F = new RectF();
+
+    public static final Property<TaskThumbnailView, Float> DIM_ALPHA =
+            new FloatProperty<TaskThumbnailView>("dimAlpha") {
+                @Override
+                public void setValue(TaskThumbnailView thumbnail, float dimAlpha) {
+                    thumbnail.setDimAlpha(dimAlpha);
+                }
+
+                @Override
+                public Float get(TaskThumbnailView thumbnailView) {
+                    return thumbnailView.mDimAlpha;
+                }
+            };
+
+    private final BaseActivity mActivity;
+    private final 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 Matrix mMatrix = new Matrix();
+
+    private float mClipBottom = -1;
+    // Contains the portion of the thumbnail that is clipped when fullscreen progress = 0.
+    private RectF mClippedInsets = new RectF();
+    private TaskView.FullscreenDrawParams mFullscreenParams;
+
+    private Task mTask;
+    private ThumbnailData mThumbnailData;
+    protected BitmapShader mBitmapShader;
+
+    private float mDimAlpha = 1f;
+    private float mDimAlphaMultiplier = 1f;
+    private float mSaturation = 1f;
+
+    private boolean mOverlayEnabled;
+    private boolean mRotated;
+
+    public TaskThumbnailView(Context context) {
+        this(context, null);
+    }
+
+    public TaskThumbnailView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public TaskThumbnailView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        mOverlay = TaskOverlayFactory.INSTANCE.get(context).createOverlay(this);
+        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);
+        mFullscreenParams = new TaskView.FullscreenDrawParams(TaskCornerRadius.get(context));
+    }
+
+    public void bind(Task task) {
+        mOverlay.reset();
+        mTask = task;
+        int color = task == null ? Color.BLACK : task.colorBackground | 0xFF000000;
+        mPaint.setColor(color);
+        mBackgroundPaint.setColor(color);
+    }
+
+    /**
+     * Updates this thumbnail.
+     */
+    public void setThumbnail(Task task, ThumbnailData thumbnailData) {
+        mTask = task;
+        if (thumbnailData != null && thumbnailData.thumbnail != null) {
+            Bitmap bm = thumbnailData.thumbnail;
+            bm.prepareToDraw();
+            mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
+            mPaint.setShader(mBitmapShader);
+            mThumbnailData = thumbnailData;
+            updateThumbnailMatrix();
+        } else {
+            mBitmapShader = null;
+            mThumbnailData = null;
+            mPaint.setShader(null);
+            mOverlay.reset();
+        }
+        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.
+     */
+    public void setDimAlpha(float dimAlpha) {
+        mDimAlpha = dimAlpha;
+        updateThumbnailPaintFilter();
+    }
+
+    public void setSaturation(float saturation) {
+        mSaturation = saturation;
+        updateThumbnailPaintFilter();
+    }
+
+    public float getDimAlpha() {
+        return mDimAlpha;
+    }
+
+    public Rect getInsets(Rect fallback) {
+        if (mThumbnailData != null) {
+            return mThumbnailData.insets;
+        }
+        return fallback;
+    }
+
+    public int getSysUiStatusNavFlags() {
+        if (mThumbnailData != null) {
+            int flags = 0;
+            flags |= (mThumbnailData.systemUiVisibility & SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0
+                    ? SystemUiController.FLAG_LIGHT_STATUS
+                    : SystemUiController.FLAG_DARK_STATUS;
+            flags |= (mThumbnailData.systemUiVisibility & SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR) != 0
+                    ? SystemUiController.FLAG_LIGHT_NAV
+                    : SystemUiController.FLAG_DARK_NAV;
+            return flags;
+        }
+        return 0;
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        RectF currentDrawnInsets = mFullscreenParams.mCurrentDrawnInsets;
+        canvas.save();
+        canvas.translate(currentDrawnInsets.left, currentDrawnInsets.top);
+        canvas.scale(mFullscreenParams.mScale, mFullscreenParams.mScale);
+        // Draw the insets if we're being drawn fullscreen (we do this for quick switch).
+        drawOnCanvas(canvas,
+                -currentDrawnInsets.left,
+                -currentDrawnInsets.top,
+                getMeasuredWidth() + currentDrawnInsets.right,
+                getMeasuredHeight() + currentDrawnInsets.bottom,
+                mFullscreenParams.mCurrentDrawnCornerRadius);
+        canvas.restore();
+    }
+
+    public RectF getInsetsToDrawInFullscreen(boolean isMultiWindowMode) {
+        // Don't show insets in multi window mode.
+        return isMultiWindowMode ? EMPTY_RECT_F : mClippedInsets;
+    }
+
+    public void setFullscreenParams(TaskView.FullscreenDrawParams fullscreenParams) {
+        mFullscreenParams = fullscreenParams;
+        invalidate();
+    }
+
+    public void drawOnCanvas(Canvas canvas, float x, float y, float width, float height,
+            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,
+                        mDimmingPaintAfterClearing);
+                return;
+            }
+        }
+
+        // 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 || mClipBottom > 0 || mThumbnailData.isTranslucent) {
+            canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mBackgroundPaint);
+            if (drawBackgroundOnly) {
+                return;
+            }
+        }
+
+        if (mClipBottom > 0) {
+            canvas.save();
+            canvas.clipRect(x, y, width, mClipBottom);
+            canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mPaint);
+            canvas.restore();
+        } else {
+            canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mPaint);
+        }
+    }
+
+    public TaskView getTaskView() {
+        return (TaskView) getParent();
+    }
+
+    public void setOverlayEnabled(boolean overlayEnabled) {
+        if (mOverlayEnabled != overlayEnabled) {
+            mOverlayEnabled = overlayEnabled;
+            updateOverlay();
+        }
+    }
+
+    private void updateOverlay() {
+        // The overlay doesn't really work when the screenshot is rotated, so don't add it.
+        if (mOverlayEnabled && !mRotated && mBitmapShader != null && mThumbnailData != null) {
+            mOverlay.initOverlay(mTask, mThumbnailData, mMatrix);
+        } else {
+            mOverlay.reset();
+        }
+    }
+
+    private void updateThumbnailPaintFilter() {
+        int mul = (int) ((1 - mDimAlpha * mDimAlphaMultiplier) * 255);
+        ColorFilter filter = getColorFilter(mul, mIsDarkTextTheme, mSaturation);
+        mBackgroundPaint.setColorFilter(filter);
+        mDimmingPaintAfterClearing.setAlpha(255 - mul);
+        if (mBitmapShader != null) {
+            mPaint.setColorFilter(filter);
+        } else {
+            mPaint.setColorFilter(null);
+            mPaint.setColor(Color.argb(255, mul, mul, mul));
+        }
+        invalidate();
+    }
+
+    private void updateThumbnailMatrix() {
+        boolean isRotated = false;
+        mClipBottom = -1;
+        if (mBitmapShader != null && mThumbnailData != null) {
+            float scale = mThumbnailData.scale;
+            Rect thumbnailInsets = mThumbnailData.insets;
+            final float thumbnailWidth = mThumbnailData.thumbnail.getWidth() -
+                    (thumbnailInsets.left + thumbnailInsets.right) * scale;
+            final float thumbnailHeight = mThumbnailData.thumbnail.getHeight() -
+                    (thumbnailInsets.top + thumbnailInsets.bottom) * scale;
+
+            final float thumbnailScale;
+            final DeviceProfile profile = mActivity.getDeviceProfile();
+
+            if (getMeasuredWidth() == 0) {
+                // If we haven't measured , skip the thumbnail drawing and only draw the background
+                // color
+                thumbnailScale = 0f;
+            } else {
+                final Configuration configuration =
+                        getContext().getResources().getConfiguration();
+                // Rotate the screenshot if not in multi-window mode
+                isRotated = FeatureFlags.OVERVIEW_USE_SCREENSHOT_ORIENTATION &&
+                        configuration.orientation != mThumbnailData.orientation &&
+                        !mActivity.isInMultiWindowMode() &&
+                        mThumbnailData.windowingMode == WINDOWING_MODE_FULLSCREEN;
+                // Scale the screenshot to always fit the width of the card.
+                thumbnailScale = isRotated
+                        ? getMeasuredWidth() / thumbnailHeight
+                        : getMeasuredWidth() / thumbnailWidth;
+            }
+
+            if (isRotated) {
+                int rotationDir = profile.isVerticalBarLayout() && !profile.isSeascape() ? -1 : 1;
+                mMatrix.setRotate(90 * rotationDir);
+                int newLeftInset = rotationDir == 1 ? thumbnailInsets.bottom : thumbnailInsets.top;
+                int newTopInset = rotationDir == 1 ? thumbnailInsets.left : thumbnailInsets.right;
+                mClippedInsets.offsetTo(newLeftInset * scale, newTopInset * scale);
+                if (rotationDir == -1) {
+                    // Crop the right/bottom side of the screenshot rather than left/top
+                    float excessHeight = thumbnailWidth * thumbnailScale - getMeasuredHeight();
+                    mClippedInsets.offset(0, excessHeight);
+                }
+                mMatrix.postTranslate(-mClippedInsets.left, -mClippedInsets.top);
+                // Move the screenshot to the thumbnail window (rotation moved it out).
+                if (rotationDir == 1) {
+                    mMatrix.postTranslate(mThumbnailData.thumbnail.getHeight(), 0);
+                } else {
+                    mMatrix.postTranslate(0, mThumbnailData.thumbnail.getWidth());
+                }
+            } else {
+                mClippedInsets.offsetTo(thumbnailInsets.left * scale, thumbnailInsets.top * scale);
+                mMatrix.setTranslate(-mClippedInsets.left, -mClippedInsets.top);
+            }
+
+            final float widthWithInsets;
+            final float heightWithInsets;
+            if (isRotated) {
+                widthWithInsets = mThumbnailData.thumbnail.getHeight() * thumbnailScale;
+                heightWithInsets = mThumbnailData.thumbnail.getWidth() * thumbnailScale;
+            } else {
+                widthWithInsets = mThumbnailData.thumbnail.getWidth() * thumbnailScale;
+                heightWithInsets = mThumbnailData.thumbnail.getHeight() * thumbnailScale;
+            }
+            mClippedInsets.left *= thumbnailScale;
+            mClippedInsets.top *= thumbnailScale;
+            mClippedInsets.right = widthWithInsets - mClippedInsets.left - getMeasuredWidth();
+            mClippedInsets.bottom = heightWithInsets - mClippedInsets.top - getMeasuredHeight();
+
+            mMatrix.postScale(thumbnailScale, thumbnailScale);
+            mBitmapShader.setLocalMatrix(mMatrix);
+
+            float bitmapHeight = Math.max((isRotated ? thumbnailWidth : thumbnailHeight)
+                    * thumbnailScale, 0);
+            if (Math.round(bitmapHeight) < getMeasuredHeight()) {
+                mClipBottom = bitmapHeight;
+            }
+            mPaint.setShader(mBitmapShader);
+        }
+
+        mRotated = isRotated;
+        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();
+    }
+
+    /**
+     * @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);
+    }
+}
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
new file mode 100644
index 0000000..b26fdce
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -0,0 +1,885 @@
+/*
+ * 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.widget.Toast.LENGTH_SHORT;
+
+import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
+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 android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.app.ActivityOptions;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Outline;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.util.FloatProperty;
+import android.util.Log;
+import android.view.Gravity;
+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.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.logging.UserEventDispatcher;
+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.launcher3.util.PendingAnimation;
+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.TaskSystemShortcut;
+import com.android.quickstep.TaskThumbnailCache;
+import com.android.quickstep.TaskUtils;
+import com.android.quickstep.util.TaskCornerRadius;
+import com.android.quickstep.views.RecentsView.PageCallbacks;
+import com.android.quickstep.views.RecentsView.ScrollState;
+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;
+
+    private static final List<Rect> SYSTEM_GESTURE_EXCLUSION_RECT =
+            Collections.singletonList(new Rect());
+
+    public static final FloatProperty<TaskView> FULLSCREEN_PROGRESS =
+            new FloatProperty<TaskView>("fullscreenProgress") {
+                @Override
+                public void setValue(TaskView taskView, float v) {
+                    taskView.setFullscreenProgress(v);
+                }
+
+                @Override
+                public Float get(TaskView taskView) {
+                    return taskView.mFullscreenProgress;
+                }
+            };
+
+    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 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 float mCornerRadius;
+    private final float mWindowCornerRadius;
+    private final BaseDraggingActivity mActivity;
+
+    private ObjectAnimator mIconAndDimAnimator;
+    private float mIconScaleAnimStartProgress = 0;
+    private float mFocusTransitionProgress = 1;
+    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;
+    public static final int INDEX_PROACTIVE_SUGGEST = 1;
+    private final FooterWrapper[] mFooters = new FooterWrapper[2];
+    private float mFooterVerticalOffset = 0;
+    private float mFooterAlpha = 1;
+    private int mStackHeight;
+
+    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 (com.android.launcher3.testing.TestProtocol.sDebugTracing) {
+                android.util.Log.d(TestProtocol.NO_START_TASK_TAG, "TaskView onClick");
+            }
+            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().logTaskLaunch(getRecentsView(),
+                    TaskUtils.getLaunchComponentKeyForTask(getTask().key));
+        });
+        mCornerRadius = TaskCornerRadius.get(context);
+        mWindowCornerRadius = QuickStepContract.getWindowCornerRadius(context.getResources());
+        mCurrentFullscreenParams = new FullscreenDrawParams(mCornerRadius);
+        mDigitalWellBeingToast = new DigitalWellBeingToast(mActivity, this);
+
+        mOutlineProvider = new TaskOutlineProvider(getResources(), mCurrentFullscreenParams);
+        setOutlineProvider(mOutlineProvider);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mSnapshotView = findViewById(R.id.snapshot);
+        mIconView = findViewById(R.id.icon);
+    }
+
+    public TaskMenuView getMenuView() {
+        return mMenuView;
+    }
+
+    public DigitalWellBeingToast getDigitalWellBeingToast() {
+        return mDigitalWellBeingToast;
+    }
+
+    /**
+     * Updates this task view to the given {@param task}.
+     */
+    public void bind(Task task) {
+        cancelPendingLoadTasks();
+        mTask = task;
+        mSnapshotView.bind(task);
+    }
+
+    public Task getTask() {
+        return mTask;
+    }
+
+    public TaskThumbnailView getThumbnail() {
+        return mSnapshotView;
+    }
+
+    public IconView getIconView() {
+        return mIconView;
+    }
+
+    public AnimatorPlaybackController createLaunchAnimationForRunningTask() {
+        final PendingAnimation pendingAnimation =
+                getRecentsView().createTaskLauncherAnimation(this, RECENTS_LAUNCH_DURATION);
+        pendingAnimation.anim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
+        AnimatorPlaybackController currentAnimation = AnimatorPlaybackController
+                .wrap(pendingAnimation.anim, RECENTS_LAUNCH_DURATION, null);
+        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 (com.android.launcher3.testing.TestProtocol.sDebugTracing) {
+            android.util.Log.d(TestProtocol.NO_START_TASK_TAG, "launchTask");
+        }
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+            if (isRunningTask()) {
+                getRecentsView().finishRecentsAnimation(false /* toRecents */,
+                        () -> resultCallbackHandler.post(() -> resultCallback.accept(true)));
+            } else {
+                launchTaskInternal(animate, freezeTaskList, resultCallback, resultCallbackHandler);
+            }
+        } else {
+            launchTaskInternal(animate, freezeTaskList, resultCallback, resultCallbackHandler);
+        }
+    }
+
+    private void launchTaskInternal(boolean animate, boolean freezeTaskList,
+            Consumer<Boolean> resultCallback, Handler resultCallbackHandler) {
+        if (com.android.launcher3.testing.TestProtocol.sDebugTracing) {
+            android.util.Log.d(TestProtocol.NO_START_TASK_TAG, "launchTaskInternal");
+        }
+        if (mTask != null) {
+            final ActivityOptions opts;
+            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);
+            }
+        }
+    }
+
+    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) {
+        getRecentsView().snapToPage(getRecentsView().indexOfChild(this));
+        mMenuView = TaskMenuView.showForTask(this);
+        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);
+        }
+    }
+
+    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);
+
+        mFooterVerticalOffset = 1.0f - scale;
+        for (FooterWrapper footer : mFooters) {
+            if (footer != null) {
+                footer.updateFooterOffset();
+            }
+        }
+    }
+
+    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);
+    }
+
+    private void resetViewTransforms() {
+        setCurveScale(1);
+        setTranslationX(0f);
+        setTranslationY(0f);
+        setTranslationZ(0);
+        setAlpha(mStableAlpha);
+        setIconScaleAndDim(1);
+    }
+
+    public void resetVisualProperties() {
+        resetViewTransforms();
+        setFullscreenProgress(0);
+    }
+
+    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) {
+        float curveInterpolation =
+                CURVE_INTERPOLATOR.getInterpolation(scrollState.linearInterpolation);
+
+        mSnapshotView.setDimAlpha(curveInterpolation * MAX_PAGE_SCRIM_ALPHA);
+        setCurveScale(getCurveScaleForCurveInterpolation(curveInterpolation));
+
+        mFooterAlpha = Utilities.boundToRange(1.0f - 2 * scrollState.linearInterpolation, 0f, 1f);
+        for (FooterWrapper footer : mFooters) {
+            if (footer != null) {
+                footer.mView.setAlpha(mFooterAlpha);
+            }
+        }
+
+        if (mMenuView != null) {
+            mMenuView.setPosition(getX() - getRecentsView().getScrollX(), getY());
+            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) view.getLayoutParams()).gravity =
+                    Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
+            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;
+    }
+
+    @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();
+            }
+        }
+        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;
+    }
+
+    public void setCurveScale(float curveScale) {
+        mCurveScale = curveScale;
+        onScaleChanged();
+    }
+
+    public float getCurveScale() {
+        return mCurveScale;
+    }
+
+    private void onScaleChanged() {
+        float scale = mCurveScale;
+        setScaleX(scale);
+        setScaleY(scale);
+    }
+
+    @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(Resources res, FullscreenDrawParams fullscreenParams) {
+            mMarginTop = res.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;
+
+            int h = view.getLayoutParams().height;
+            if (h > 0) {
+                mExpectedHeight = h;
+            } else {
+                int m = MeasureSpec.makeMeasureSpec(MeasureSpec.EXACTLY - 1, MeasureSpec.AT_MOST);
+                view.measure(m, m);
+                mExpectedHeight = view.getMeasuredHeight();
+            }
+            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() {
+            mAnimationOffset = Math.round(mStackHeight * mFooterVerticalOffset);
+            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();
+        }
+    }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+
+        info.addAction(
+                new AccessibilityNodeInfo.AccessibilityAction(R.string.accessibility_close_task,
+                        getContext().getText(R.string.accessibility_close_task)));
+
+        final Context context = getContext();
+        final List<TaskSystemShortcut> shortcuts =
+                TaskOverlayFactory.INSTANCE.get(getContext()).getEnabledShortcuts(this);
+        final int count = shortcuts.size();
+        for (int i = 0; i < count; ++i) {
+            final TaskSystemShortcut menuOption = shortcuts.get(i);
+            OnClickListener onClickListener = menuOption.getOnClickListener(mActivity, this);
+            if (onClickListener != null) {
+                info.addAction(menuOption.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.getChildCount() - recentsView.indexOfChild(this) - 1, 1,
+                        false);
+        info.setCollectionItemInfo(itemInfo);
+    }
+
+    @Override
+    public boolean performAccessibilityAction(int action, Bundle arguments) {
+        if (action == R.string.accessibility_close_task) {
+            getRecentsView().dismissTask(this, true /*animateTaskView*/,
+                    true /*removeTask*/);
+            return true;
+        }
+
+        if (action == R.string.accessibility_app_usage_settings) {
+            mDigitalWellBeingToast.openAppUsageSettings(this);
+            return true;
+        }
+
+        final List<TaskSystemShortcut> shortcuts =
+                TaskOverlayFactory.INSTANCE.get(getContext()).getEnabledShortcuts(this);
+        final int count = shortcuts.size();
+        for (int i = 0; i < count; ++i) {
+            final TaskSystemShortcut menuOption = shortcuts.get(i);
+            if (menuOption.hasHandlerForAction(action)) {
+                OnClickListener onClickListener = menuOption.getOnClickListener(mActivity, this);
+                if (onClickListener != null) {
+                    onClickListener.onClick(this);
+                }
+                return true;
+            }
+        }
+
+        return super.performAccessibilityAction(action, arguments);
+    }
+
+    public RecentsView getRecentsView() {
+        return (RecentsView) getParent();
+    }
+
+    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);
+        if (progress == mFullscreenProgress) {
+            return;
+        }
+        mFullscreenProgress = progress;
+        boolean isFullscreen = mFullscreenProgress > 0;
+        mIconView.setVisibility(progress < 1 ? VISIBLE : INVISIBLE);
+        setClipChildren(!isFullscreen);
+        setClipToPadding(!isFullscreen);
+
+        TaskThumbnailView thumbnail = getThumbnail();
+        boolean isMultiWindowMode = mActivity.getDeviceProfile().isMultiWindowMode;
+        RectF insets = thumbnail.getInsetsToDrawInFullscreen(isMultiWindowMode);
+        float currentInsetsLeft = insets.left * mFullscreenProgress;
+        float currentInsetsRight = insets.right * mFullscreenProgress;
+        mCurrentFullscreenParams.setInsets(currentInsetsLeft,
+                insets.top * mFullscreenProgress,
+                currentInsetsRight,
+                insets.bottom * mFullscreenProgress);
+        float fullscreenCornerRadius = isMultiWindowMode ? 0 : mWindowCornerRadius;
+        mCurrentFullscreenParams.setCornerRadius(Utilities.mapRange(mFullscreenProgress,
+                mCornerRadius, fullscreenCornerRadius) / getRecentsView().getScaleX());
+        // 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 (getWidth() > 0) {
+            mCurrentFullscreenParams.setScale(getWidth()
+                    / (getWidth() + currentInsetsLeft + currentInsetsRight));
+        }
+
+        // Some of the items in here are dependent on the current fullscreen params
+        setIconScaleAndDim(progress, true /* invert */);
+
+        thumbnail.setFullscreenParams(mCurrentFullscreenParams);
+        mOutlineProvider.setFullscreenParams(mCurrentFullscreenParams);
+        invalidateOutline();
+    }
+
+    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)}.
+     */
+    static class FullscreenDrawParams {
+        RectF mCurrentDrawnInsets = new RectF();
+        float mCurrentDrawnCornerRadius;
+        /** The current scale we apply to the thumbnail to adjust for new left/right insets. */
+        float mScale = 1;
+
+        public FullscreenDrawParams(float cornerRadius) {
+            setCornerRadius(cornerRadius);
+        }
+
+        public void setInsets(float left, float top, float right, float bottom) {
+            mCurrentDrawnInsets.set(left, top, right, bottom);
+        }
+
+        public void setCornerRadius(float cornerRadius) {
+            mCurrentDrawnCornerRadius = cornerRadius;
+        }
+
+        public void setScale(float scale) {
+            mScale = scale;
+        }
+    }
+}
diff --git a/quickstep/res/drawable/bg_wellbeing_toast.xml b/quickstep/res/drawable/bg_wellbeing_toast.xml
new file mode 100644
index 0000000..65730f6
--- /dev/null
+++ b/quickstep/res/drawable/bg_wellbeing_toast.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+    <solid android:color="#E61A73E8" />
+    <corners android:radius="?android:attr/dialogCornerRadius" />
+</shape>
\ No newline at end of file
diff --git a/quickstep/res/drawable/ic_hourglass_top.xml b/quickstep/res/drawable/ic_hourglass_top.xml
new file mode 100644
index 0000000..2f8bec3
--- /dev/null
+++ b/quickstep/res/drawable/ic_hourglass_top.xml
@@ -0,0 +1,13 @@
+<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">
+    <group>
+        <clip-path android:pathData="M0,0H24V24H0Z M 0,0"/>
+        <path
+            android:fillColor="#FFFFFFFF"
+            android:pathData="M6,2V8H6l4,4L6,16H6v6H18V16h0l-4,-4,4,-4h0V2ZM16,16.5V20H8V16.5l4,-4Z"/>
+    </group>
+</vector>
diff --git a/quickstep/res/drawable/ic_pin.xml b/quickstep/res/drawable/ic_pin.xml
index 8c799e3..f9eedbc 100644
--- a/quickstep/res/drawable/ic_pin.xml
+++ b/quickstep/res/drawable/ic_pin.xml
@@ -18,9 +18,16 @@
         android:width="24.0dp"
         android:height="24.0dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/textColorPrimary">
 
     <path
-        android:fillColor="#FFffffff"
-        android:pathData="M16,12L16,4l1,0L17,2L7,2l0,2l1,0l0,8l-2,2l0,2l5.2,0l0,6l1.6,0l0,-6L18,16l0,-2L16,12z"/>
+        android:strokeColor="#FFFFFFFF"
+        android:strokeAlpha=".008"
+        android:pathData="M0 0h24v24H0z" />
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M17 4v7l2 3v2h-6v5l-1 1-1-1v-5H5v-2l2-3V4c0-1.1 0.9 -2 2-2h6c1.11 0 2 0.89 2
+2zM9 4v7.75L7.5 14h9L15 11.75V4H9z" />
+
 </vector>
\ No newline at end of file
diff --git a/quickstep/res/drawable/ic_split_screen.xml b/quickstep/res/drawable/ic_split_screen.xml
index 110af91..1080069 100644
--- a/quickstep/res/drawable/ic_split_screen.xml
+++ b/quickstep/res/drawable/ic_split_screen.xml
@@ -17,7 +17,8 @@
         android:width="24dp"
         android:height="24dp"
         android:viewportWidth="24"
-        android:viewportHeight="24">
+        android:viewportHeight="24"
+        android:tint="?android:attr/textColorPrimary">
 
     <path
         android:fillColor="@android:color/white"
diff --git a/quickstep/res/drawable/task_menu_bg.xml b/quickstep/res/drawable/task_menu_bg.xml
index d5597a9..7334d98 100644
--- a/quickstep/res/drawable/task_menu_bg.xml
+++ b/quickstep/res/drawable/task_menu_bg.xml
@@ -28,8 +28,8 @@
         <!-- Background -->
         <shape>
             <corners
-                android:topLeftRadius="@dimen/task_corner_radius"
-                android:topRightRadius="@dimen/task_corner_radius"
+                android:topLeftRadius="?android:attr/dialogCornerRadius"
+                android:topRightRadius="?android:attr/dialogCornerRadius"
                 android:bottomLeftRadius="0dp"
                 android:bottomRightRadius="0dp" />
             <solid android:color="?attr/popupColorPrimary" />
diff --git a/quickstep/res/layout/digital_wellbeing_toast.xml b/quickstep/res/layout/digital_wellbeing_toast.xml
new file mode 100644
index 0000000..83594e4
--- /dev/null
+++ b/quickstep/res/layout/digital_wellbeing_toast.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+<TextView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    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:textSize="14sp"/>
\ No newline at end of file
diff --git a/quickstep/res/layout/overview_clear_all_button.xml b/quickstep/res/layout/overview_clear_all_button.xml
index ea7a494..fc06ba0 100644
--- a/quickstep/res/layout/overview_clear_all_button.xml
+++ b/quickstep/res/layout/overview_clear_all_button.xml
@@ -17,6 +17,7 @@
 <com.android.quickstep.views.ClearAllButton
     xmlns:android="http://schemas.android.com/apk/res/android"
     style="@android:style/Widget.DeviceDefault.Button.Borderless"
+    android:id="@+id/clear_all"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:text="@string/recents_clear_all"
diff --git a/quickstep/res/layout/task.xml b/quickstep/res/layout/task.xml
index 36d327d..60cfa0c 100644
--- a/quickstep/res/layout/task.xml
+++ b/quickstep/res/layout/task.xml
@@ -25,7 +25,7 @@
         android:id="@+id/snapshot"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:layout_marginTop="@dimen/task_thumbnail_top_margin" />
+        android:layout_marginTop="@dimen/task_thumbnail_top_margin"/>
 
     <com.android.quickstep.views.IconView
         android:id="@+id/icon"
@@ -33,5 +33,5 @@
         android:layout_height="@dimen/task_thumbnail_icon_size"
         android:layout_gravity="top|center_horizontal"
         android:focusable="false"
-        android:importantForAccessibility="no" />
+        android:importantForAccessibility="no"/>
 </com.android.quickstep.views.TaskView>
\ No newline at end of file
diff --git a/quickstep/res/layout/task_menu.xml b/quickstep/res/layout/task_menu.xml
index bf55ece..098b34f 100644
--- a/quickstep/res/layout/task_menu.xml
+++ b/quickstep/res/layout/task_menu.xml
@@ -24,11 +24,19 @@
     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_icon_and_name"
+        android:id="@+id/task_name"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:drawablePadding="@dimen/deep_shortcut_drawable_padding"
         android:gravity="center_horizontal"
         android:layout_marginBottom="16dp"
         android:textSize="12sp"/>
diff --git a/quickstep/res/values-af/strings.xml b/quickstep/res/values-af/strings.xml
index 8ae493f..64b8e2c 100644
--- a/quickstep/res/values-af/strings.xml
+++ b/quickstep/res/values-af/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Oorsig"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Geen onlangse items nie"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Maak toe"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Programgebruikinstellings"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Vee alles uit"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Onlangse programme"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minuut"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> oor vandag"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Programvoorstelle"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Alle programme"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Jou voorspelde programme"</string>
 </resources>
diff --git a/quickstep/res/values-am/strings.xml b/quickstep/res/values-am/strings.xml
index 4ca0c67..3daa922 100644
--- a/quickstep/res/values-am/strings.xml
+++ b/quickstep/res/values-am/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"ማጠቃለያ"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"ምንም የቅርብ ጊዜ ንጥሎች የሉም"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"ዝጋ"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"የመተግበሪያ አጠቃቀም ቅንብሮች"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"ሁሉንም አጽዳ"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"የቅርብ ጊዜ መተግበሪያዎች"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>፣ <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 ደቂቃ"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"ዛሬ <xliff:g id="TIME">%1$s</xliff:g> ቀርቷል"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"የመተግበሪያ ጥቆማዎች"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"ሁሉም መተግበሪያዎች"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"የእርስዎ የሚገመቱ መተግበሪያዎች"</string>
 </resources>
diff --git a/quickstep/res/values-ar/strings.xml b/quickstep/res/values-ar/strings.xml
index c04b618..b036bc1 100644
--- a/quickstep/res/values-ar/strings.xml
+++ b/quickstep/res/values-ar/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"نظرة عامة"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"ليست هناك عناصر تم استخدامها مؤخرًا"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"إغلاق"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"إعدادات استخدام التطبيق"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"محو الكل"</string>
-    <string name="accessibility_recent_apps" msgid="4058661986695117371">"التطبيقات التي تمّ استخدامها مؤخرًا"</string>
+    <string name="accessibility_recent_apps" msgid="4058661986695117371">"التطبيقات المستخدمة مؤخرًا"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>، <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"أقل من دقيقة"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"يتبقى اليوم <xliff:g id="TIME">%1$s</xliff:g>."</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"اقتراحات التطبيقات"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"جميع التطبيقات"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"تطبيقاتك المتوقّعة"</string>
 </resources>
diff --git a/quickstep/res/values-as/strings.xml b/quickstep/res/values-as/strings.xml
index 812a246..c188493 100644
--- a/quickstep/res/values-as/strings.xml
+++ b/quickstep/res/values-as/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"অৱলোকন"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"কোনো শেহতীয়া বস্তু নাই"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"বন্ধ কৰক"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"এপে ব্যৱহাৰ কৰা ডেটাৰ ছেটিংসমূহ"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"সকলো মচক"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"শেহতীয়া এপসমূহ"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; ১ মিনিট"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"আজি <xliff:g id="TIME">%1$s</xliff:g> বাকী আছ"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"এপৰ পৰামৰ্শসমূহ"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"সকলো এপ্"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"আপোনাৰ অনুমানিক এপ্"</string>
 </resources>
diff --git a/quickstep/res/values-az-rAZ/strings.xml b/quickstep/res/values-az-rAZ/strings.xml
deleted file mode 100644
index 0546f46..0000000
--- a/quickstep/res/values-az-rAZ/strings.xml
+++ /dev/null
@@ -1,28 +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 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="accessibility_desc_recent_apps" msgid="1444379410873162882">"İcmal"</string>
-    <string name="recents_empty_message" msgid="7040467240571714191">"Son elementlər yoxdur"</string>
-    <string name="accessibility_close_task" msgid="5354563209433803643">"Bağlayın"</string>
-    <string name="recents_clear_all" msgid="5328176793634888831">"Hamısını silin"</string>
-</resources>
diff --git a/quickstep/res/values-az/strings.xml b/quickstep/res/values-az/strings.xml
index 832ed8c..aa8fa53 100644
--- a/quickstep/res/values-az/strings.xml
+++ b/quickstep/res/values-az/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"İcmal"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Son elementlər yoxdur"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Bağlayın"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Tətbiq istifadə ayarları"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Hamısını silin"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Son tətbiqlər"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 dəq"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Bu gün <xliff:g id="TIME">%1$s</xliff:g> qaldı"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Tətbiq təklifləri"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Bütün tətbiqlər"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Təklif edilən tətbiqlər"</string>
 </resources>
diff --git a/quickstep/res/values-b+sr+Latn/strings.xml b/quickstep/res/values-b+sr+Latn/strings.xml
index fc4c8c0..fbbe9d2 100644
--- a/quickstep/res/values-b+sr+Latn/strings.xml
+++ b/quickstep/res/values-b+sr+Latn/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Pregled"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Nema nedavnih stavki"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Zatvori"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Podešavanja korišćenja aplikacije"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Obriši sve"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Nedavne aplikacije"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Još <xliff:g id="TIME">%1$s</xliff:g> danas"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Predlozi aplikacija"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Sve aplikacije"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Predviđene aplikacije"</string>
 </resources>
diff --git a/quickstep/res/values-be-rBY/strings.xml b/quickstep/res/values-be-rBY/strings.xml
deleted file mode 100644
index 1e60dd3..0000000
--- a/quickstep/res/values-be-rBY/strings.xml
+++ /dev/null
@@ -1,28 +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 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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Агляд"</string>
-    <string name="recents_empty_message" msgid="7040467240571714191">"Няма новых элементаў"</string>
-    <string name="accessibility_close_task" msgid="5354563209433803643">"Закрыць"</string>
-    <string name="recents_clear_all" msgid="5328176793634888831">"Ачысціць усё"</string>
-</resources>
diff --git a/quickstep/res/values-be/strings.xml b/quickstep/res/values-be/strings.xml
index c5d03d4..c4a2772 100644
--- a/quickstep/res/values-be/strings.xml
+++ b/quickstep/res/values-be/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Агляд"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Няма новых элементаў"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Закрыць"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Налады выкарыстання праграмы"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Ачысціць усё"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Нядаўнія праграмы"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 хв"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Сёння засталося <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Прапановы праграм"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Усе праграмы"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Вашы праграмы з падказак"</string>
 </resources>
diff --git a/quickstep/res/values-bg/strings.xml b/quickstep/res/values-bg/strings.xml
index 2672b83..9e8c54a 100644
--- a/quickstep/res/values-bg/strings.xml
+++ b/quickstep/res/values-bg/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Общ преглед"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Няма скорошни елементи"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Затваряне"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Настройки за използването на приложенията"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Изчистване на всички"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Скорошни приложения"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 мин"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Оставащо време днес: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Предложения за приложения"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Всички приложения"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Предвидени приложения"</string>
 </resources>
diff --git a/quickstep/res/values-bn-rBD/strings.xml b/quickstep/res/values-bn-rBD/strings.xml
deleted file mode 100644
index a0605d8..0000000
--- a/quickstep/res/values-bn-rBD/strings.xml
+++ /dev/null
@@ -1,28 +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 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="accessibility_desc_recent_apps" msgid="1444379410873162882">"এক নজরে"</string>
-    <string name="recents_empty_message" msgid="7040467240571714191">"কোনো সাম্প্রতিক আইটেম নেই"</string>
-    <string name="accessibility_close_task" msgid="5354563209433803643">"বন্ধ করুন"</string>
-    <string name="recents_clear_all" msgid="5328176793634888831">"সবকিছু খালি করুন"</string>
-</resources>
diff --git a/quickstep/res/values-bn/strings.xml b/quickstep/res/values-bn/strings.xml
index 0a824c2..57f92e5 100644
--- a/quickstep/res/values-bn/strings.xml
+++ b/quickstep/res/values-bn/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"এক নজরে"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"কোনো সাম্প্রতিক আইটেম নেই"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"বন্ধ করুন"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"অ্যাপ ব্যবহারের সেটিংস"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"সবকিছু খালি করুন"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"সম্প্রতি ব্যবহৃত অ্যাপ"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; ১ মি."</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"আজকে <xliff:g id="TIME">%1$s</xliff:g> বাকি আছে"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"অ্যাপের সাজেশন"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"সব অ্যাপ"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"আপনার প্রয়োজন হতে পারে এমন অ্যাপ"</string>
 </resources>
diff --git a/quickstep/res/values-bs-rBA/strings.xml b/quickstep/res/values-bs-rBA/strings.xml
deleted file mode 100644
index 9ffa848..0000000
--- a/quickstep/res/values-bs-rBA/strings.xml
+++ /dev/null
@@ -1,28 +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 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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Pregled"</string>
-    <string name="recents_empty_message" msgid="7040467240571714191">"Nema nedavnih stavki"</string>
-    <string name="accessibility_close_task" msgid="5354563209433803643">"Zatvaranje"</string>
-    <string name="recents_clear_all" msgid="5328176793634888831">"Obriši sve"</string>
-</resources>
diff --git a/quickstep/res/values-bs/strings.xml b/quickstep/res/values-bs/strings.xml
index 6bf38eb..7968f7c 100644
--- a/quickstep/res/values-bs/strings.xml
+++ b/quickstep/res/values-bs/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Pregled"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Nema nedavnih stavki"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Zatvaranje"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Postavke korištenja aplikacije"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Obriši sve"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Nedavne aplikacije"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Preostalo vrijeme: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Prijedlozi za aplikacije"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Sve aplikacije"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Predviđene aplikacije"</string>
 </resources>
diff --git a/quickstep/res/values-ca/strings.xml b/quickstep/res/values-ca/strings.xml
index 2d51703..6420aa8 100644
--- a/quickstep/res/values-ca/strings.xml
+++ b/quickstep/res/values-ca/strings.xml
@@ -19,11 +19,19 @@
 
 <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">"Divideix la pantalla"</string>
+    <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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Aplicacions recents"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"No hi ha cap element recent"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Tanca"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Configuració d\'ús d\'aplicacions"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Esborra-ho tot"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplicacions recents"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>; <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minut"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"temps restant avui: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Suggeriments d\'aplicacions"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Totes les aplicacions"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Prediccions d\'aplicacions"</string>
 </resources>
diff --git a/quickstep/res/values-cs/strings.xml b/quickstep/res/values-cs/strings.xml
index a178df0..194ff87 100644
--- a/quickstep/res/values-cs/strings.xml
+++ b/quickstep/res/values-cs/strings.xml
@@ -21,9 +21,17 @@
     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_freeform" msgid="48863056265284071">"Neomezený režim"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Přehled"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Žádné nedávné položky"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Zavřít"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Nastavení využití aplikací"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Vymazat vše"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Poslední aplikace"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minuta"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"dnes zbývá: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Návrhy aplikací"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Všechny aplikace"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Vaše předpovídané aplikace"</string>
 </resources>
diff --git a/quickstep/res/values-da/strings.xml b/quickstep/res/values-da/strings.xml
index d0d629f..b43a76e 100644
--- a/quickstep/res/values-da/strings.xml
+++ b/quickstep/res/values-da/strings.xml
@@ -19,11 +19,19 @@
 
 <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 skærm"</string>
+    <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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Oversigt"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Ingen nye elementer"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Luk"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Indstillinger for appforbrug"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Ryd alt"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Seneste apps"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> tilbage i dag"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Appforslag"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Alle apps"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Dine foreslåede apps"</string>
 </resources>
diff --git a/quickstep/res/values-de/strings.xml b/quickstep/res/values-de/strings.xml
index aee8b85..449cc8c 100644
--- a/quickstep/res/values-de/strings.xml
+++ b/quickstep/res/values-de/strings.xml
@@ -19,11 +19,19 @@
 
 <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">"Bildschirm teilen"</string>
-    <string name="recent_task_option_pin" msgid="7929860679018978258">"Fixieren"</string>
+    <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Splitscreen"</string>
+    <string name="recent_task_option_pin" msgid="7929860679018978258">"Anpinnen"</string>
+    <string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform-Modus"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Übersicht"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Keine kürzlich verwendeten Elemente"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Schließen"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Einstellungen zur App-Nutzung"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Alle Apps schließen"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Zuletzt aktive Apps"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Heute noch <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"App-Vorschläge"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Alle Apps"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"App-Vorschläge für dich"</string>
 </resources>
diff --git a/quickstep/res/values-el/strings.xml b/quickstep/res/values-el/strings.xml
index 7364b82..87268df 100644
--- a/quickstep/res/values-el/strings.xml
+++ b/quickstep/res/values-el/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Επισκόπηση"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Δεν υπάρχουν πρόσφατα στοιχεία"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Κλείσιμο"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Ρυθμίσεις χρήσης εφαρμογής"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Διαγραφή όλων"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Πρόσφατες εφαρμογές"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 λ."</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Απομένουν <xliff:g id="TIME">%1$s</xliff:g> σήμερα"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Προτάσεις εφαρμογών"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Όλες οι εφαρμογές"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Προβλέψεις εφαρμογών"</string>
 </resources>
diff --git a/quickstep/res/values-en-rAU/strings.xml b/quickstep/res/values-en-rAU/strings.xml
index d0dc1e8..2d1418e 100644
--- a/quickstep/res/values-en-rAU/strings.xml
+++ b/quickstep/res/values-en-rAU/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Overview"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"No recent items"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Close"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"App usage settings"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Clear all"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Recent apps"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minute"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> left today"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"App suggestions"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"All apps"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Your predicted apps"</string>
 </resources>
diff --git a/quickstep/res/values-en-rGB/strings.xml b/quickstep/res/values-en-rGB/strings.xml
index d0dc1e8..2d1418e 100644
--- a/quickstep/res/values-en-rGB/strings.xml
+++ b/quickstep/res/values-en-rGB/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Overview"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"No recent items"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Close"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"App usage settings"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Clear all"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Recent apps"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minute"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> left today"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"App suggestions"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"All apps"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Your predicted apps"</string>
 </resources>
diff --git a/quickstep/res/values-en-rIN/strings.xml b/quickstep/res/values-en-rIN/strings.xml
index d0dc1e8..2d1418e 100644
--- a/quickstep/res/values-en-rIN/strings.xml
+++ b/quickstep/res/values-en-rIN/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Overview"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"No recent items"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Close"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"App usage settings"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Clear all"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Recent apps"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minute"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> left today"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"App suggestions"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"All apps"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Your predicted apps"</string>
 </resources>
diff --git a/quickstep/res/values-es-rUS/strings.xml b/quickstep/res/values-es-rUS/strings.xml
index 83e9d96..5f5d0bd 100644
--- a/quickstep/res/values-es-rUS/strings.xml
+++ b/quickstep/res/values-es-rUS/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Recientes"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"No hay elementos recientes"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Cerrar"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Configuración de uso de la app"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Borrar todo"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Apps recientes"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> (<xliff:g id="REMAINING_TIME">%2$s</xliff:g>)"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minuto"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Tiempo restante: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Sugerencias de apps"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Todas las apps"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Predicción de tus apps"</string>
 </resources>
diff --git a/quickstep/res/values-es/strings.xml b/quickstep/res/values-es/strings.xml
index e076df5..329286b 100644
--- a/quickstep/res/values-es/strings.xml
+++ b/quickstep/res/values-es/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Aplicaciones recientes"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"No hay elementos recientes"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Cerrar"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Ajustes de uso de la aplicación"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Borrar todo"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplicaciones recientes"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> (<xliff:g id="REMAINING_TIME">%2$s</xliff:g>)"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt;1 minuto"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"tiempo restante: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Sugerencias de aplicaciones"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Todas las aplicaciones"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Predicción de aplicaciones"</string>
 </resources>
diff --git a/quickstep/res/values-et-rEE/strings.xml b/quickstep/res/values-et-rEE/strings.xml
deleted file mode 100644
index efa68f9..0000000
--- a/quickstep/res/values-et-rEE/strings.xml
+++ /dev/null
@@ -1,28 +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 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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Ülevaade"</string>
-    <string name="recents_empty_message" msgid="7040467240571714191">"Hiljutisi üksusi pole"</string>
-    <string name="accessibility_close_task" msgid="5354563209433803643">"Sule"</string>
-    <string name="recents_clear_all" msgid="5328176793634888831">"Sule kõik"</string>
-</resources>
diff --git a/quickstep/res/values-et/strings.xml b/quickstep/res/values-et/strings.xml
index 85de9c0..0577b0f 100644
--- a/quickstep/res/values-et/strings.xml
+++ b/quickstep/res/values-et/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Ülevaade"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Hiljutisi üksusi pole"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Sule"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Rakenduse kasutuse seaded"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Sule kõik"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Hiljutised rakendused"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minut"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Tääna jäänud <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Rakenduste soovitused"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Kõik rakendused"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Teie ennustatud rakendused"</string>
 </resources>
diff --git a/quickstep/res/values-eu-rES/strings.xml b/quickstep/res/values-eu-rES/strings.xml
deleted file mode 100644
index d22242e..0000000
--- a/quickstep/res/values-eu-rES/strings.xml
+++ /dev/null
@@ -1,28 +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 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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Ikuspegi orokorra"</string>
-    <string name="recents_empty_message" msgid="7040467240571714191">"Ez dago azkenaldi honetako ezer"</string>
-    <string name="accessibility_close_task" msgid="5354563209433803643">"Itxi"</string>
-    <string name="recents_clear_all" msgid="5328176793634888831">"Garbitu guztiak"</string>
-</resources>
diff --git a/quickstep/res/values-eu/strings.xml b/quickstep/res/values-eu/strings.xml
index 60943cc..c2d149e 100644
--- a/quickstep/res/values-eu/strings.xml
+++ b/quickstep/res/values-eu/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Ikuspegi orokorra"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Ez dago azkenaldi honetako ezer"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Itxi"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Aplikazioen erabileraren ezarpenak"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Garbitu guztiak"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Azken aplikazioak"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> (<xliff:g id="REMAINING_TIME">%2$s</xliff:g>)"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> gelditzen dira gaur"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Iradokitako aplikazioak"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Aplikazio guztiak"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Lagungarri izan dakizkizukeen aplikazioak"</string>
 </resources>
diff --git a/quickstep/res/values-fa/strings.xml b/quickstep/res/values-fa/strings.xml
index 4a30daa..cc26695 100644
--- a/quickstep/res/values-fa/strings.xml
+++ b/quickstep/res/values-fa/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"نمای کلی"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"بدون موارد اخیر"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"بستن"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"تنظیمات استفاده از برنامه"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"پاک کردن همه"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"برنامه‌های اخیر"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>، <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; ۱ دقیقه"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> باقی‌مانده برای امروز"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"برنامه‌های پیشنهادی"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"همه برنامه‌ها"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"برنامه‌های پیش‌بینی‌شده"</string>
 </resources>
diff --git a/quickstep/res/values-fi/strings.xml b/quickstep/res/values-fi/strings.xml
index 8f41350..f43433e 100644
--- a/quickstep/res/values-fi/strings.xml
+++ b/quickstep/res/values-fi/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Viimeisimmät"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Ei viimeaikaisia kohteita"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Sulje"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Sovelluksen käyttöasetukset"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Poista kaikki"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Viimeisimmät sovellukset"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> jäljellä tänään"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Sovellusehdotukset"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Kaikki sovellukset"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Sovellusennusteet"</string>
 </resources>
diff --git a/quickstep/res/values-fr-rCA/strings.xml b/quickstep/res/values-fr-rCA/strings.xml
index 7177996..a9a1cff 100644
--- a/quickstep/res/values-fr-rCA/strings.xml
+++ b/quickstep/res/values-fr-rCA/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Aperçu"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Aucun élément récent"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Fermer"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Paramètres d\'utilisation de l\'application"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Tout effacer"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Applications récentes"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> : <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Il reste <xliff:g id="TIME">%1$s</xliff:g> aujourd\'hui"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Suggestions d\'applications"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Toutes les applications"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Vos prédictions d\'applications"</string>
 </resources>
diff --git a/quickstep/res/values-fr/strings.xml b/quickstep/res/values-fr/strings.xml
index 0deb00b..5394f49 100644
--- a/quickstep/res/values-fr/strings.xml
+++ b/quickstep/res/values-fr/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Aperçu"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Aucun élément récent"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Fermer"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Paramètres de consommation de l\'application"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Tout effacer"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Applications récentes"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Encore <xliff:g id="TIME">%1$s</xliff:g> aujourd\'hui"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Suggestions d\'applications"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Toutes les applications"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Vos applications prévues"</string>
 </resources>
diff --git a/quickstep/res/values-gl-rES/strings.xml b/quickstep/res/values-gl-rES/strings.xml
deleted file mode 100644
index 8efc773..0000000
--- a/quickstep/res/values-gl-rES/strings.xml
+++ /dev/null
@@ -1,28 +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 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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Visión xeral"</string>
-    <string name="recents_empty_message" msgid="7040467240571714191">"Non hai elementos recentes"</string>
-    <string name="accessibility_close_task" msgid="5354563209433803643">"Pecha a aplicación"</string>
-    <string name="recents_clear_all" msgid="5328176793634888831">"Borrar todo"</string>
-</resources>
diff --git a/quickstep/res/values-gl/strings.xml b/quickstep/res/values-gl/strings.xml
index 8c217eb..356d10d 100644
--- a/quickstep/res/values-gl/strings.xml
+++ b/quickstep/res/values-gl/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Visión xeral"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Non hai elementos recentes"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Pecha a aplicación"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Configuración do uso de aplicacións"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Borrar todo"</string>
-    <string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplicacións recentes"</string>
+    <string name="accessibility_recent_apps" msgid="4058661986695117371">"Apps recentes"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> (<xliff:g id="REMAINING_TIME">%2$s</xliff:g>)"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt;1 min"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Tempo restante hoxe <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Suxestións de aplicacións"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Todas as aplicacións"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"As túas aplicacións preditas"</string>
 </resources>
diff --git a/quickstep/res/values-gu-rIN/strings.xml b/quickstep/res/values-gu-rIN/strings.xml
deleted file mode 100644
index cdae86f..0000000
--- a/quickstep/res/values-gu-rIN/strings.xml
+++ /dev/null
@@ -1,28 +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 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="accessibility_desc_recent_apps" msgid="1444379410873162882">"ઝલક"</string>
-    <string name="recents_empty_message" msgid="7040467240571714191">"તાજેતરની કોઈ આઇટમ નથી"</string>
-    <string name="accessibility_close_task" msgid="5354563209433803643">"બંધ કરો"</string>
-    <string name="recents_clear_all" msgid="5328176793634888831">"બધું સાફ કરો"</string>
-</resources>
diff --git a/quickstep/res/values-gu/strings.xml b/quickstep/res/values-gu/strings.xml
index 8b9a538..660ad87 100644
--- a/quickstep/res/values-gu/strings.xml
+++ b/quickstep/res/values-gu/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"ઝલક"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"તાજેતરની કોઈ આઇટમ નથી"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"બંધ કરો"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ઍપ વપરાશનું સેટિંગ"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"બધું સાફ કરો"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"તાજેતરની ઍપ"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 મિનિટ"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> આજે બાકી"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"ઍપ સૂચનો"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"બધી ઍપ"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"તમારી પૂર્વાનુમાનિત ઍપ"</string>
 </resources>
diff --git a/quickstep/res/values-hi/strings.xml b/quickstep/res/values-hi/strings.xml
index 83c31e7..0467af4 100644
--- a/quickstep/res/values-hi/strings.xml
+++ b/quickstep/res/values-hi/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"खास जानकारी"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"हाल ही में इस्तेमाल किया गया कोई ऐप्लिकेशन नहीं है"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"बंद करें"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ऐप्लिकेशन इस्तेमाल की सेटिंग"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"सभी ऐप्लिकेशन बंद करें"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"हाल ही में इस्तेमाल किए गए एेप्लिकेशन"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt;1 मिनट"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"आज <xliff:g id="TIME">%1$s</xliff:g> और चलेगा"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"ऐप्लिकेशन के सुझाव"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"सभी ऐप्लिकेशन"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"आपके काम के ऐप्लिकेशन"</string>
 </resources>
diff --git a/quickstep/res/values-hr/strings.xml b/quickstep/res/values-hr/strings.xml
index baa8d85..ab56e57 100644
--- a/quickstep/res/values-hr/strings.xml
+++ b/quickstep/res/values-hr/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Pregled"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Nema nedavnih stavki"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Zatvori"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Postavke upotrebe aplikacija"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Izbriši sve"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Nedavne aplikacije"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Još <xliff:g id="TIME">%1$s</xliff:g> danas"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Predložene aplikacije"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Sve aplikacije"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Vaše predviđene aplikacije"</string>
 </resources>
diff --git a/quickstep/res/values-hu/strings.xml b/quickstep/res/values-hu/strings.xml
index d971374..dec6ea0 100644
--- a/quickstep/res/values-hu/strings.xml
+++ b/quickstep/res/values-hu/strings.xml
@@ -21,9 +21,17 @@
     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">"Rögzítés"</string>
+    <string name="recent_task_option_freeform" msgid="48863056265284071">"Szabad forma"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Áttekintés"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Nincsenek mostanában használt elemek"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Bezárás"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Alkalmazáshasználati beállítások"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Összes törlése"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Legutóbbi alkalmazások"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 perc"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Ma még <xliff:g id="TIME">%1$s</xliff:g> van hátra"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Alkalmazásjavaslatok"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Az összes alkalmazás"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Várható alkalmazások"</string>
 </resources>
diff --git a/quickstep/res/values-hy-rAM/strings.xml b/quickstep/res/values-hy-rAM/strings.xml
deleted file mode 100644
index 4afae7d..0000000
--- a/quickstep/res/values-hy-rAM/strings.xml
+++ /dev/null
@@ -1,28 +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 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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Ընդհանուր տեղեկություններ"</string>
-    <string name="recents_empty_message" msgid="7040467240571714191">"Վերջին տարրեր չկան"</string>
-    <string name="accessibility_close_task" msgid="5354563209433803643">"Փակել"</string>
-    <string name="recents_clear_all" msgid="5328176793634888831">"Փակել բոլորը"</string>
-</resources>
diff --git a/quickstep/res/values-hy/strings.xml b/quickstep/res/values-hy/strings.xml
index b4b9b98..1656a14 100644
--- a/quickstep/res/values-hy/strings.xml
+++ b/quickstep/res/values-hy/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Ընդհանուր տեղեկություններ"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Վերջին տարրեր չկան"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Փակել"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Հավելվածի օգտագործման կարգավորումներ"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Փակել բոլորը"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Վերջին օգտագործած հավելվածները"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 ր"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Այսօր մնացել է՝ <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Առաջարկվող հավելվածներ"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Բոլոր հավելվածները"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Ձեր կանխատեսված հավելվածները"</string>
 </resources>
diff --git a/quickstep/res/values-in/strings.xml b/quickstep/res/values-in/strings.xml
index 787c143..6824d16 100644
--- a/quickstep/res/values-in/strings.xml
+++ b/quickstep/res/values-in/strings.xml
@@ -21,9 +21,17 @@
     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_freeform" msgid="48863056265284071">"Format bebas"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Ringkasan"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Tidak ada item yang baru dibuka"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Tutup"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Setelan penggunaan aplikasi"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Hapus semua"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplikasi baru-baru ini"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 menit"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> tersisa hari ini"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Saran aplikasi"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Semua aplikasi"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Aplikasi yang diprediksi"</string>
 </resources>
diff --git a/quickstep/res/values-is-rIS/strings.xml b/quickstep/res/values-is-rIS/strings.xml
deleted file mode 100644
index 88a92ed..0000000
--- a/quickstep/res/values-is-rIS/strings.xml
+++ /dev/null
@@ -1,28 +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 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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Yfirlit"</string>
-    <string name="recents_empty_message" msgid="7040467240571714191">"Engin nýleg atriði"</string>
-    <string name="accessibility_close_task" msgid="5354563209433803643">"Loka"</string>
-    <string name="recents_clear_all" msgid="5328176793634888831">"Hreinsa allt"</string>
-</resources>
diff --git a/quickstep/res/values-is/strings.xml b/quickstep/res/values-is/strings.xml
index 1aface0..f60a2c6 100644
--- a/quickstep/res/values-is/strings.xml
+++ b/quickstep/res/values-is/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Yfirlit"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Engin nýleg atriði"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Loka"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Notkunarstillingar forrits"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Hreinsa allt"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Nýleg forrit"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 mín."</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> eftir í dag"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Tillögur að forritum"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Öll forrit"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Spáð forrit"</string>
 </resources>
diff --git a/quickstep/res/values-it/strings.xml b/quickstep/res/values-it/strings.xml
index 192ec44..559fdb4 100644
--- a/quickstep/res/values-it/strings.xml
+++ b/quickstep/res/values-it/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Panoramica"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Nessun elemento recente"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Chiudi"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Impostazioni di utilizzo delle app"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Cancella tutto"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"App recenti"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Rimanente oggi: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"App suggerite"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Tutte le app"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Le app previste"</string>
 </resources>
diff --git a/quickstep/res/values-iw/strings.xml b/quickstep/res/values-iw/strings.xml
index 64c35ec..58cab4e 100644
--- a/quickstep/res/values-iw/strings.xml
+++ b/quickstep/res/values-iw/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"מסכים אחרונים"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"אין פריטים אחרונים"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"סגירה"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"הגדרות שימוש באפליקציה"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"ניקוי הכול"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"אפליקציות אחרונות"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"‏&lt; דקה"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"הזמן שנותר להיום: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"הצעות לאפליקציות"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"כל האפליקציות"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"האפליקציות החזויות שלך"</string>
 </resources>
diff --git a/quickstep/res/values-ja/strings.xml b/quickstep/res/values-ja/strings.xml
index 558112c..d3fecde 100644
--- a/quickstep/res/values-ja/strings.xml
+++ b/quickstep/res/values-ja/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"概要"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"最近のアイテムはありません"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"閉じる"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"アプリの使用状況の設定"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"すべてクリア"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"最近使ったアプリ"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>、<xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"1 分未満"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"今日はあと <xliff:g id="TIME">%1$s</xliff:g>です"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"アプリの候補"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"すべてのアプリ"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"予測されたアプリ"</string>
 </resources>
diff --git a/quickstep/res/values-ka-rGE/strings.xml b/quickstep/res/values-ka-rGE/strings.xml
deleted file mode 100644
index 6de8ed9..0000000
--- a/quickstep/res/values-ka-rGE/strings.xml
+++ /dev/null
@@ -1,28 +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 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="accessibility_desc_recent_apps" msgid="1444379410873162882">"მიმოხილვა"</string>
-    <string name="recents_empty_message" msgid="7040467240571714191">"ბოლოს გამოყენებული ერთეულები არ არის"</string>
-    <string name="accessibility_close_task" msgid="5354563209433803643">"დახურვა"</string>
-    <string name="recents_clear_all" msgid="5328176793634888831">"ყველას გასუფთავება"</string>
-</resources>
diff --git a/quickstep/res/values-ka/strings.xml b/quickstep/res/values-ka/strings.xml
index 5f061de..67b03a7 100644
--- a/quickstep/res/values-ka/strings.xml
+++ b/quickstep/res/values-ka/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"მიმოხილვა"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"ბოლოს გამოყენებული ერთეულები არ არის"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"დახურვა"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"აპების გამოყენების პარამეტრები"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"ყველას გასუფთავება"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"ბოლოდროინდელი აპები"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 წუთი"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"დღეს დარჩენილია <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"აპების შემოთავაზებები"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"ყველა აპი"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"თქვენი პროგნოზირებული აპები"</string>
 </resources>
diff --git a/quickstep/res/values-kk-rKZ/strings.xml b/quickstep/res/values-kk-rKZ/strings.xml
deleted file mode 100644
index ddd4a77..0000000
--- a/quickstep/res/values-kk-rKZ/strings.xml
+++ /dev/null
@@ -1,28 +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 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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Шолу"</string>
-    <string name="recents_empty_message" msgid="7040467240571714191">"Соңғы элементтер жоқ"</string>
-    <string name="accessibility_close_task" msgid="5354563209433803643">"Жабу"</string>
-    <string name="recents_clear_all" msgid="5328176793634888831">"Барлығын өшіру"</string>
-</resources>
diff --git a/quickstep/res/values-kk/strings.xml b/quickstep/res/values-kk/strings.xml
index 3e72130..a9fcbed 100644
--- a/quickstep/res/values-kk/strings.xml
+++ b/quickstep/res/values-kk/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Шолу"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Соңғы элементтер жоқ"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Жабу"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Қолданбаны пайдалану параметрлері"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Барлығын өшіру"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Соңғы пайдаланылған қолданбалар"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 мин"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Бүгін <xliff:g id="TIME">%1$s</xliff:g> қалды"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Қолданба ұсыныстары"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Барлық қолданбалар"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Ұсынылатын қолданбалар"</string>
 </resources>
diff --git a/quickstep/res/values-km-rKH/strings.xml b/quickstep/res/values-km-rKH/strings.xml
deleted file mode 100644
index 65c1dcc..0000000
--- a/quickstep/res/values-km-rKH/strings.xml
+++ /dev/null
@@ -1,28 +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 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="accessibility_desc_recent_apps" msgid="1444379410873162882">"ទិដ្ឋភាពរួម"</string>
-    <string name="recents_empty_message" msgid="7040467240571714191">"មិនមានធាតុថ្មីៗទេ"</string>
-    <string name="accessibility_close_task" msgid="5354563209433803643">"បិទ"</string>
-    <string name="recents_clear_all" msgid="5328176793634888831">"សម្អាត​ទាំងអស់"</string>
-</resources>
diff --git a/quickstep/res/values-km/strings.xml b/quickstep/res/values-km/strings.xml
index 6ae9482..c422041 100644
--- a/quickstep/res/values-km/strings.xml
+++ b/quickstep/res/values-km/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"ទិដ្ឋភាពរួម"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"មិនមានធាតុថ្មីៗទេ"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"បិទ"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ការកំណត់​ការប្រើប្រាស់​កម្មវិធី"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"សម្អាត​ទាំងអស់"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"កម្មវិធី​ថ្មីៗ"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 នាទី"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"នៅសល់ <xliff:g id="TIME">%1$s</xliff:g> ទៀត​នៅថ្ងៃនេះ"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"ការណែនាំកម្មវិធី"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"កម្មវិធី​ទាំងអស់"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"កម្មវិធី​ដែលបាន​ព្យាករ​របស់អ្នក"</string>
 </resources>
diff --git a/quickstep/res/values-kn-rIN/strings.xml b/quickstep/res/values-kn-rIN/strings.xml
deleted file mode 100644
index 55ccbb0..0000000
--- a/quickstep/res/values-kn-rIN/strings.xml
+++ /dev/null
@@ -1,28 +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 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="accessibility_desc_recent_apps" msgid="1444379410873162882">"ಅವಲೋಕನ"</string>
-    <string name="recents_empty_message" msgid="7040467240571714191">"ಯಾವುದೇ ಇತ್ತೀಚಿನ ಐಟಂಗಳಿಲ್ಲ"</string>
-    <string name="accessibility_close_task" msgid="5354563209433803643">"ಮುಚ್ಚಿ"</string>
-    <string name="recents_clear_all" msgid="5328176793634888831">"ಎಲ್ಲವನ್ನೂ ತೆರವುಗೊಳಿಸಿ"</string>
-</resources>
diff --git a/quickstep/res/values-kn/strings.xml b/quickstep/res/values-kn/strings.xml
index 591418b..5278261 100644
--- a/quickstep/res/values-kn/strings.xml
+++ b/quickstep/res/values-kn/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"ಅವಲೋಕನ"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"ಯಾವುದೇ ಇತ್ತೀಚಿನ ಐಟಂಗಳಿಲ್ಲ"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"ಮುಚ್ಚಿ"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ಆ್ಯಪ್‌ ಬಳಕೆಯ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"ಎಲ್ಲವನ್ನೂ ತೆರವುಗೊಳಿಸಿ"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"ಇತ್ತೀಚಿನ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 ನಿ"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"ಇಂದು <xliff:g id="TIME">%1$s</xliff:g> ಸಮಯ ಉಳಿದಿದೆ"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"ಆ್ಯಪ್ ಸಲಹೆಗಳು"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"ಎಲ್ಲಾ ಆ್ಯಪ್‌ಗಳು"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"ನಿಮ್ಮ ಸಂಭವನೀಯ ಆ್ಯಪ್‌ಗಳು"</string>
 </resources>
diff --git a/quickstep/res/values-ko/strings.xml b/quickstep/res/values-ko/strings.xml
index 5b1ecbd..7a8e6a1 100644
--- a/quickstep/res/values-ko/strings.xml
+++ b/quickstep/res/values-ko/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"최근 사용"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"최근 항목이 없습니다."</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"닫기"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"앱 사용 설정"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"모두 삭제"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"최근 앱"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1분"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"오늘 <xliff:g id="TIME">%1$s</xliff:g> 남음"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"앱 추천"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"모든 앱"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"추천 앱"</string>
 </resources>
diff --git a/quickstep/res/values-ky-rKG/strings.xml b/quickstep/res/values-ky-rKG/strings.xml
deleted file mode 100644
index b788693..0000000
--- a/quickstep/res/values-ky-rKG/strings.xml
+++ /dev/null
@@ -1,28 +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 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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Сереп салуу"</string>
-    <string name="recents_empty_message" msgid="7040467240571714191">"Акыркы колдонмолор жок"</string>
-    <string name="accessibility_close_task" msgid="5354563209433803643">"Жабуу"</string>
-    <string name="recents_clear_all" msgid="5328176793634888831">"Баарын тазалоо"</string>
-</resources>
diff --git a/quickstep/res/values-ky/strings.xml b/quickstep/res/values-ky/strings.xml
index 9f84037..4018e57 100644
--- a/quickstep/res/values-ky/strings.xml
+++ b/quickstep/res/values-ky/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Сереп салуу"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Акыркы колдонмолор жок"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Жабуу"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Колдонмону пайдалануу жөндөөлөрү"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Баарын тазалоо"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Акыркы колдонмолор"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 мүнөт"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Бүгүн <xliff:g id="TIME">%1$s</xliff:g> мүнөт калды"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Колдонмо сунуштары"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Бардык колдонмолор"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Божомолдонгон колдонмолоруңуз"</string>
 </resources>
diff --git a/quickstep/res/values-lo-rLA/strings.xml b/quickstep/res/values-lo-rLA/strings.xml
deleted file mode 100644
index a83743a..0000000
--- a/quickstep/res/values-lo-rLA/strings.xml
+++ /dev/null
@@ -1,28 +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 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="accessibility_desc_recent_apps" msgid="1444379410873162882">"ພາບຮວມ"</string>
-    <string name="recents_empty_message" msgid="7040467240571714191">"ບໍ່ມີລາຍການຫຼ້າສຸດ"</string>
-    <string name="accessibility_close_task" msgid="5354563209433803643">"ປິດ"</string>
-    <string name="recents_clear_all" msgid="5328176793634888831">"ລຶບລ້າງທັງໝົດ"</string>
-</resources>
diff --git a/quickstep/res/values-lo/strings.xml b/quickstep/res/values-lo/strings.xml
index ed1cb6e..e406b70 100644
--- a/quickstep/res/values-lo/strings.xml
+++ b/quickstep/res/values-lo/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"ພາບຮວມ"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"ບໍ່ມີລາຍການຫຼ້າສຸດ"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"ປິດ"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ການຕັ້ງຄ່າການນຳໃຊ້ແອັບ"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"ລຶບລ້າງທັງໝົດ"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"ແອັບຫຼ້າສຸດ"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 ນາທີ"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"ເຫຼືອ <xliff:g id="TIME">%1$s</xliff:g> ມື້ນີ້"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"ການແນະນຳແອັບ"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"ແອັບທັງໝົດ"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"ແອັບທີ່ຄາດເດົາໄວ້ແລ້ວຂອງທ່ານ"</string>
 </resources>
diff --git a/quickstep/res/values-lt/strings.xml b/quickstep/res/values-lt/strings.xml
index c16fc56..ed1fc37 100644
--- a/quickstep/res/values-lt/strings.xml
+++ b/quickstep/res/values-lt/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Apžvalga"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Nėra jokių naujausių elementų"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Uždaryti"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Programos naudojimo nustatymai"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Išvalyti viską"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Naujausios programos"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min."</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Šiandien liko: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Programų pasiūlymai"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Visos programos"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Numatomos programos"</string>
 </resources>
diff --git a/quickstep/res/values-lv/strings.xml b/quickstep/res/values-lv/strings.xml
index 2dee41c..85ce0e0 100644
--- a/quickstep/res/values-lv/strings.xml
+++ b/quickstep/res/values-lv/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Pārskats"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Nav nesenu vienumu."</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Aizvērt"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Lietotņu izmantošanas iestatījumi"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Notīrīt visu"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Pēdējās izmantotās lietotnes"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt;1 minūte"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Šodien atlicis: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Ieteicamās lietotnes"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Visas lietotnes"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Jūsu prognozētās lietotnes"</string>
 </resources>
diff --git a/quickstep/res/values-mk-rMK/strings.xml b/quickstep/res/values-mk-rMK/strings.xml
deleted file mode 100644
index e428b4e..0000000
--- a/quickstep/res/values-mk-rMK/strings.xml
+++ /dev/null
@@ -1,28 +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 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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Преглед"</string>
-    <string name="recents_empty_message" msgid="7040467240571714191">"Нема неодамнешни ставки"</string>
-    <string name="accessibility_close_task" msgid="5354563209433803643">"Затвори"</string>
-    <string name="recents_clear_all" msgid="5328176793634888831">"Исчисти ги сите"</string>
-</resources>
diff --git a/quickstep/res/values-mk/strings.xml b/quickstep/res/values-mk/strings.xml
index ff16cea..9f11521 100644
--- a/quickstep/res/values-mk/strings.xml
+++ b/quickstep/res/values-mk/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Преглед"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Нема неодамнешни ставки"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Затвори"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Поставки за користење на апликациите"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Исчисти ги сите"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Неодамнешни апликации"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 минута"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Уште <xliff:g id="TIME">%1$s</xliff:g> за денес"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Предлози за апликации"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Сите апликации"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Вашите предвидени апликации"</string>
 </resources>
diff --git a/quickstep/res/values-ml-rIN/strings.xml b/quickstep/res/values-ml-rIN/strings.xml
deleted file mode 100644
index 4cca447..0000000
--- a/quickstep/res/values-ml-rIN/strings.xml
+++ /dev/null
@@ -1,28 +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 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="accessibility_desc_recent_apps" msgid="1444379410873162882">"അവലോകനം"</string>
-    <string name="recents_empty_message" msgid="7040467240571714191">"സമീപകാല ഇനങ്ങൾ ഒന്നുമില്ല"</string>
-    <string name="accessibility_close_task" msgid="5354563209433803643">"അവസാനിപ്പിക്കുക"</string>
-    <string name="recents_clear_all" msgid="5328176793634888831">"എല്ലാം മായ്‌ക്കുക"</string>
-</resources>
diff --git a/quickstep/res/values-ml/strings.xml b/quickstep/res/values-ml/strings.xml
index 624aded..2e02e80 100644
--- a/quickstep/res/values-ml/strings.xml
+++ b/quickstep/res/values-ml/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"അവലോകനം"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"സമീപകാല ഇനങ്ങൾ ഒന്നുമില്ല"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"അവസാനിപ്പിക്കുക"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ആപ്പ് ഉപയോഗ ക്രമീകരണം"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"എല്ലാം മായ്‌ക്കുക"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"സമീപകാല ആപ്പുകൾ"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 മിനിറ്റ്"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"ഇന്ന് <xliff:g id="TIME">%1$s</xliff:g> ശേഷിക്കുന്നു"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"ആപ്പ് നിർദ്ദേശങ്ങൾ"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"എല്ലാ ആപ്പുകളും"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"നിങ്ങളുടെ പ്രവചിക്കപ്പെട്ട ആപ്പുകൾ"</string>
 </resources>
diff --git a/quickstep/res/values-mn-rMN/strings.xml b/quickstep/res/values-mn-rMN/strings.xml
deleted file mode 100644
index f40f69e..0000000
--- a/quickstep/res/values-mn-rMN/strings.xml
+++ /dev/null
@@ -1,28 +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 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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Тойм"</string>
-    <string name="recents_empty_message" msgid="7040467240571714191">"Сүүлийн үеийн зүйл алга"</string>
-    <string name="accessibility_close_task" msgid="5354563209433803643">"Хаах"</string>
-    <string name="recents_clear_all" msgid="5328176793634888831">"Бүгдийг устгах"</string>
-</resources>
diff --git a/quickstep/res/values-mn/strings.xml b/quickstep/res/values-mn/strings.xml
index f902125..5de8602 100644
--- a/quickstep/res/values-mn/strings.xml
+++ b/quickstep/res/values-mn/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Тойм"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Сүүлийн үеийн зүйл алга"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Хаах"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Апп ашиглалтын тохиргоо"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Бүгдийг устгах"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Саяхны аппууд"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 минут"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Өнөөдөр <xliff:g id="TIME">%1$s</xliff:g> үлдсэн"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Аппын зөвлөмж"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Бүх апп"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Таны таамагласан аппууд"</string>
 </resources>
diff --git a/quickstep/res/values-mr-rIN/strings.xml b/quickstep/res/values-mr-rIN/strings.xml
deleted file mode 100644
index 938363d..0000000
--- a/quickstep/res/values-mr-rIN/strings.xml
+++ /dev/null
@@ -1,28 +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 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="accessibility_desc_recent_apps" msgid="1444379410873162882">"अवलोकन"</string>
-    <string name="recents_empty_message" msgid="7040467240571714191">"कोणतेही अलीकडील आयटम नाहीत"</string>
-    <string name="accessibility_close_task" msgid="5354563209433803643">"बंद"</string>
-    <string name="recents_clear_all" msgid="5328176793634888831">"सर्व साफ करा"</string>
-</resources>
diff --git a/quickstep/res/values-mr/strings.xml b/quickstep/res/values-mr/strings.xml
index 7a669dd..1ca558a 100644
--- a/quickstep/res/values-mr/strings.xml
+++ b/quickstep/res/values-mr/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"अवलोकन"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"कोणतेही अलीकडील आयटम नाहीत"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"बंद"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"अ‍ॅप वापर सेटिंग्ज"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"सर्व साफ करा"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"अलीकडील अॅप्स"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"१मिहून कमी"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"आज <xliff:g id="TIME">%1$s</xliff:g>शिल्लक आहे"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"अ‍ॅप सूचना"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"सर्व अ‍ॅप्स"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"तुमची पूर्वानुमानीत अ‍ॅप्स"</string>
 </resources>
diff --git a/quickstep/res/values-ms-rMY/strings.xml b/quickstep/res/values-ms-rMY/strings.xml
deleted file mode 100644
index 236fab2..0000000
--- a/quickstep/res/values-ms-rMY/strings.xml
+++ /dev/null
@@ -1,28 +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 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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Ikhtisar"</string>
-    <string name="recents_empty_message" msgid="7040467240571714191">"Tiada item terbaharu"</string>
-    <string name="accessibility_close_task" msgid="5354563209433803643">"Tutup"</string>
-    <string name="recents_clear_all" msgid="5328176793634888831">"Kosongkan semua"</string>
-</resources>
diff --git a/quickstep/res/values-ms/strings.xml b/quickstep/res/values-ms/strings.xml
index 6995863..2542963 100644
--- a/quickstep/res/values-ms/strings.xml
+++ b/quickstep/res/values-ms/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Ikhtisar"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Tiada item terbaharu"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Tutup"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Tetapan penggunaan apl"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Kosongkan semua"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Apl terbaharu"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minit"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> lagi hari ini"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Cadangan apl"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Semua apl"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Apl ramalan anda"</string>
 </resources>
diff --git a/quickstep/res/values-my-rMM/strings.xml b/quickstep/res/values-my-rMM/strings.xml
deleted file mode 100644
index e44b904..0000000
--- a/quickstep/res/values-my-rMM/strings.xml
+++ /dev/null
@@ -1,28 +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 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="accessibility_desc_recent_apps" msgid="1444379410873162882">"အနှစ်ချုပ်"</string>
-    <string name="recents_empty_message" msgid="7040467240571714191">"မကြာမီကဖွင့်ထားသည်များ မရှိပါ"</string>
-    <string name="accessibility_close_task" msgid="5354563209433803643">"ပိတ်ရန်"</string>
-    <string name="recents_clear_all" msgid="5328176793634888831">"အားလုံးကို ရှင်းရန်"</string>
-</resources>
diff --git a/quickstep/res/values-my/strings.xml b/quickstep/res/values-my/strings.xml
index ae6dc7d..7683e05 100644
--- a/quickstep/res/values-my/strings.xml
+++ b/quickstep/res/values-my/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"အနှစ်ချုပ်"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"မကြာမီကဖွင့်ထားသည်များ မရှိပါ"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"ပိတ်ရန်"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"အက်ပ်အသုံးပြုမှု ဆက်တင်များ"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"အားလုံးကို ရှင်းရန်"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"လတ်တလောသုံး အက်ပ်များ"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>၊ <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; ၁ မိနစ်"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"ယနေ့ <xliff:g id="TIME">%1$s</xliff:g> ခု ကျန်သည်"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"အက်ပ်အကြံပြုချက်များ"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"အက်ပ်အားလုံး"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"သင်၏ ခန့်မှန်းအက်ပ်များ"</string>
 </resources>
diff --git a/quickstep/res/values-nb/strings.xml b/quickstep/res/values-nb/strings.xml
index cb8ee10..01bbb6a 100644
--- a/quickstep/res/values-nb/strings.xml
+++ b/quickstep/res/values-nb/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Oversikt"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Ingen nylige elementer"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Lukk"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Innstillinger for appbruk"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Fjern alt"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Nylige apper"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minutt"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> gjenstår i dag"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Appanbefalinger"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Alle apper"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Forslag til apper"</string>
 </resources>
diff --git a/quickstep/res/values-ne-rNP/strings.xml b/quickstep/res/values-ne-rNP/strings.xml
deleted file mode 100644
index bf52604..0000000
--- a/quickstep/res/values-ne-rNP/strings.xml
+++ /dev/null
@@ -1,28 +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 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="accessibility_desc_recent_apps" msgid="1444379410873162882">"परिदृश्य"</string>
-    <string name="recents_empty_message" msgid="7040467240571714191">"हालसालैको कुनै पनि वस्तु छैन"</string>
-    <string name="accessibility_close_task" msgid="5354563209433803643">"बन्द गर्नुहोस्"</string>
-    <string name="recents_clear_all" msgid="5328176793634888831">"सबै खाली गर्नुहोस्"</string>
-</resources>
diff --git a/quickstep/res/values-ne/strings.xml b/quickstep/res/values-ne/strings.xml
index 4efae7b..60e9bd5 100644
--- a/quickstep/res/values-ne/strings.xml
+++ b/quickstep/res/values-ne/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"परिदृश्य"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"हालसालैको कुनै पनि वस्तु छैन"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"बन्द गर्नुहोस्"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"अनुप्रयोगको उपयोगका सेटिङहरू"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"सबै खाली गर्नुहोस्"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"हालसालैका अनुप्रयोगहरू"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; १ मिनेट"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"आज: <xliff:g id="TIME">%1$s</xliff:g> बाँकी"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"अनुप्रयोगसम्बन्धी सुझावहरू"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"सबै अनुप्रयोगहरू"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"तपाईंका पूर्वानुमानित अनुप्रयोगहरू"</string>
 </resources>
diff --git a/quickstep/res/values-nl/strings.xml b/quickstep/res/values-nl/strings.xml
index 8ef2a5d..8032567 100644
--- a/quickstep/res/values-nl/strings.xml
+++ b/quickstep/res/values-nl/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Overzicht"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Geen recente items"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Sluiten"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Instellingen voor app-gebruik"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Alles wissen"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Recente apps"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minuut"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Nog <xliff:g id="TIME">%1$s</xliff:g> vandaag"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"App-suggesties"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Alle apps"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Je voorspelde apps"</string>
 </resources>
diff --git a/quickstep/res/values-or/strings.xml b/quickstep/res/values-or/strings.xml
index 6895ef5..2ebec4e 100644
--- a/quickstep/res/values-or/strings.xml
+++ b/quickstep/res/values-or/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"ସଂକ୍ଷିପ୍ତ ବିବରଣ"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"କୌଣସି ସାମ୍ପ୍ରତିକ ଆଇଟମ୍ ନାହିଁ"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"ବନ୍ଦ କରନ୍ତୁ"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ଆପ୍‍ ବ୍ୟବହାର ସେଟିଂସ୍‍"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"ସବୁ ଖାଲି କରନ୍ତୁ"</string>
-    <string name="accessibility_recent_apps" msgid="4058661986695117371">"ସାମ୍ପ୍ରତିକ ଆପ୍‌"</string>
+    <string name="accessibility_recent_apps" msgid="4058661986695117371">"ବର୍ତ୍ତମାନର ଆପ୍‌"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 ମିନିଟ୍"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"ଆଜି <xliff:g id="TIME">%1$s</xliff:g> ବାକି ଅଛି"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"ଆପ୍ ପରାମର୍ଶଗୁଡ଼ିକ"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"ସମସ୍ତ ଆପ୍ସ"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"ଆପଣ ପୂର୍ବାନୁମାନ କରିଥିବା ଆପ୍ସ"</string>
 </resources>
diff --git a/quickstep/res/values-pa-rIN/strings.xml b/quickstep/res/values-pa-rIN/strings.xml
deleted file mode 100644
index bc044c8..0000000
--- a/quickstep/res/values-pa-rIN/strings.xml
+++ /dev/null
@@ -1,28 +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 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="accessibility_desc_recent_apps" msgid="1444379410873162882">"ਰੂਪ-ਰੇਖਾ"</string>
-    <string name="recents_empty_message" msgid="7040467240571714191">"ਕੋਈ ਹਾਲੀਆ ਆਈਟਮਾਂ ਨਹੀਂ"</string>
-    <string name="accessibility_close_task" msgid="5354563209433803643">"ਬੰਦ ਕਰੋ"</string>
-    <string name="recents_clear_all" msgid="5328176793634888831">"ਸਭ ਕਲੀਅਰ ਕਰੋ"</string>
-</resources>
diff --git a/quickstep/res/values-pa/strings.xml b/quickstep/res/values-pa/strings.xml
index 4159c30..58c0d2a 100644
--- a/quickstep/res/values-pa/strings.xml
+++ b/quickstep/res/values-pa/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"ਰੂਪ-ਰੇਖਾ"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"ਕੋਈ ਹਾਲੀਆ ਆਈਟਮਾਂ ਨਹੀਂ"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"ਬੰਦ ਕਰੋ"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ਐਪ ਵਰਤੋਂ ਦੀਆਂ ਸੈਟਿੰਗਾਂ"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"ਸਭ ਕਲੀਅਰ ਕਰੋ"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"ਹਾਲੀਆ ਐਪਾਂ"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 ਮਿੰਟ"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"ਅੱਜ <xliff:g id="TIME">%1$s</xliff:g> ਬਾਕੀ"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"ਐਪ ਸੰਬੰਧੀ ਸੁਝਾਅ"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"ਸਾਰੀਆਂ ਐਪਾਂ"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"ਤੁਹਾਡੀਆਂ ਪੂਰਵ ਅਨੁਮਾਨਿਤ ਐਪਾਂ"</string>
 </resources>
diff --git a/quickstep/res/values-pl/strings.xml b/quickstep/res/values-pl/strings.xml
index cf15abd..d83160d 100644
--- a/quickstep/res/values-pl/strings.xml
+++ b/quickstep/res/values-pl/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Przegląd"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Brak ostatnich elementów"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Zamknij"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Ustawienia użycia aplikacji"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Wyczyść wszystko"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Ostatnie aplikacje"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&gt; 1 min"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Na dziś zostało <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Sugerowane aplikacje"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Wszystkie aplikacje"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Przewidywane aplikacje"</string>
 </resources>
diff --git a/quickstep/res/values-pt-rPT/strings.xml b/quickstep/res/values-pt-rPT/strings.xml
index 36c7e3c..2fd34d6 100644
--- a/quickstep/res/values-pt-rPT/strings.xml
+++ b/quickstep/res/values-pt-rPT/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Vista geral"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Nenhum item recente"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Fechar"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Definições de utilização de aplicações"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Limpar tudo"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplicações recentes"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minuto"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Resta(m) <xliff:g id="TIME">%1$s</xliff:g> hoje."</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Sugestões de aplicações"</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>
 </resources>
diff --git a/quickstep/res/values-pt/strings.xml b/quickstep/res/values-pt/strings.xml
index 41f53f0..673dfe2 100644
--- a/quickstep/res/values-pt/strings.xml
+++ b/quickstep/res/values-pt/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Visão geral"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Nenhum item recente"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Fechar"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Configurações de uso do app"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Limpar tudo"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Apps recentes"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> restante(s) hoje"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Sugestões de apps"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Todos os apps"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Suas predições de apps"</string>
 </resources>
diff --git a/quickstep/res/values-ro/strings.xml b/quickstep/res/values-ro/strings.xml
index 032d886..2ac783e 100644
--- a/quickstep/res/values-ro/strings.xml
+++ b/quickstep/res/values-ro/strings.xml
@@ -19,11 +19,19 @@
 
 <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 divizat"</string>
+    <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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Recente"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Niciun element recent"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Închideți"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Setări de utilizare a aplicației"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Ștergeți tot"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplicații recente"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minut"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Au mai rămas <xliff:g id="TIME">%1$s</xliff:g> astăzi"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Sugestii de aplicații"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Toate aplicațiile"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Aplicațiile estimate"</string>
 </resources>
diff --git a/quickstep/res/values-ru/strings.xml b/quickstep/res/values-ru/strings.xml
index 6a218fc..5dd89a6 100644
--- a/quickstep/res/values-ru/strings.xml
+++ b/quickstep/res/values-ru/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Обзор"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Здесь пока ничего нет."</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Закрыть"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Настройки использования приложения"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Очистить все"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Недавние приложения"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>: <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 мин."</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Осталось сегодня: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Рекомендуемые приложения"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Все приложения"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Ваши рекомендуемые приложения"</string>
 </resources>
diff --git a/quickstep/res/values-si-rLK/strings.xml b/quickstep/res/values-si-rLK/strings.xml
deleted file mode 100644
index 61bb8ba..0000000
--- a/quickstep/res/values-si-rLK/strings.xml
+++ /dev/null
@@ -1,28 +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 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="accessibility_desc_recent_apps" msgid="1444379410873162882">"දළ විශ්ලේෂණය"</string>
-    <string name="recents_empty_message" msgid="7040467240571714191">"මෑත අයිතම නැත"</string>
-    <string name="accessibility_close_task" msgid="5354563209433803643">"වසන්න"</string>
-    <string name="recents_clear_all" msgid="5328176793634888831">"සියල්ල හිස් කරන්න"</string>
-</resources>
diff --git a/quickstep/res/values-si/strings.xml b/quickstep/res/values-si/strings.xml
index c01211a..f6584c4 100644
--- a/quickstep/res/values-si/strings.xml
+++ b/quickstep/res/values-si/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"දළ විශ්ලේෂණය"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"මෑත අයිතම නැත"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"වසන්න"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"යෙදුම් භාවිත සැකසීම්"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"සියල්ල හිස් කරන්න"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"මෑත යෙදුම්"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 විනාඩියක්"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"අද <xliff:g id="TIME">%1$s</xliff:g>ක් ඉතුරුයි"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"යෙදුම් යෝජනා"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"සියලු යෙදුම්"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"ඔබේ පුරෝකථන කළ යෙදුම්"</string>
 </resources>
diff --git a/quickstep/res/values-sk/strings.xml b/quickstep/res/values-sk/strings.xml
index 2cd3942..8a9c736 100644
--- a/quickstep/res/values-sk/strings.xml
+++ b/quickstep/res/values-sk/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Prehľad"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Žiadne nedávne položky"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Zavrieť"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Nastavenia využívania aplikácie"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Vymazať všetko"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Nedávne aplikácie"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"Menej ako 1 minúta"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Dnes ešte zostáva: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Návrhy aplikácií"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Všetky aplikácie"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Vaše predpovedané aplikácie"</string>
 </resources>
diff --git a/quickstep/res/values-sl/strings.xml b/quickstep/res/values-sl/strings.xml
index 06eb95d..15f8f89 100644
--- a/quickstep/res/values-sl/strings.xml
+++ b/quickstep/res/values-sl/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Pregled"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Ni nedavnih elementov"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Zapri"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Nastavitve uporabe aplikacij"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Počisti vse"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Nedavne aplikacije"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Danes je ostalo še <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Predlogi za aplikacije"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Vse aplikacije"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Predvidene aplikacije"</string>
 </resources>
diff --git a/quickstep/res/values-sq-rAL/strings.xml b/quickstep/res/values-sq-rAL/strings.xml
deleted file mode 100644
index a0c3d78..0000000
--- a/quickstep/res/values-sq-rAL/strings.xml
+++ /dev/null
@@ -1,28 +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 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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Përmbledhja"</string>
-    <string name="recents_empty_message" msgid="7040467240571714191">"Nuk ka asnjë artikull të fundit"</string>
-    <string name="accessibility_close_task" msgid="5354563209433803643">"Mbyll"</string>
-    <string name="recents_clear_all" msgid="5328176793634888831">"Pastroji të gjitha"</string>
-</resources>
diff --git a/quickstep/res/values-sq/strings.xml b/quickstep/res/values-sq/strings.xml
index 00231e1..d8f5f28 100644
--- a/quickstep/res/values-sq/strings.xml
+++ b/quickstep/res/values-sq/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Përmbledhja"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Nuk ka asnjë artikull të fundit"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Mbyll"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Cilësimet e përdorimit të aplikacionit"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Pastroji të gjitha"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplikacionet e fundit"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minutë"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> të mbetura sot"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Sugjerimet e aplikacioneve"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Të gjitha aplikacionet"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Aplikacionet e tua të parashikuara"</string>
 </resources>
diff --git a/quickstep/res/values-sr/strings.xml b/quickstep/res/values-sr/strings.xml
index 0077ee2..b721641 100644
--- a/quickstep/res/values-sr/strings.xml
+++ b/quickstep/res/values-sr/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Преглед"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Нема недавних ставки"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Затвори"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Подешавања коришћења апликације"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Обриши све"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Недавне апликације"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 мин"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Још <xliff:g id="TIME">%1$s</xliff:g> данас"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Предлози апликација"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Све апликације"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Предвиђене апликације"</string>
 </resources>
diff --git a/quickstep/res/values-sv/strings.xml b/quickstep/res/values-sv/strings.xml
index f05d79f..ba7ebcd 100644
--- a/quickstep/res/values-sv/strings.xml
+++ b/quickstep/res/values-sv/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Översikt"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Listan med de senaste åtgärderna är tom"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Stäng"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Inställningar för appanvändning"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Rensa alla"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Senaste apparna"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> kvar i dag"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Appförslag"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Alla appar"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Föreslagna appar"</string>
 </resources>
diff --git a/quickstep/res/values-sw/strings.xml b/quickstep/res/values-sw/strings.xml
index e6ce953..24db429 100644
--- a/quickstep/res/values-sw/strings.xml
+++ b/quickstep/res/values-sw/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Muhtasari"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Hakuna vipengee vya hivi karibuni"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Funga"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Mipangilio ya matumizi ya programu"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Ondoa zote"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Programu za hivi karibuni"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; dak 1"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Umebakisha <xliff:g id="TIME">%1$s</xliff:g> leo"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Mapendekezo ya programu"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Programu zote"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Programu zako zinazopendekezwa"</string>
 </resources>
diff --git a/quickstep/res/values-ta-rIN/strings.xml b/quickstep/res/values-ta-rIN/strings.xml
deleted file mode 100644
index 0c800ca..0000000
--- a/quickstep/res/values-ta-rIN/strings.xml
+++ /dev/null
@@ -1,28 +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 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="accessibility_desc_recent_apps" msgid="1444379410873162882">"மேலோட்டப் பார்வை"</string>
-    <string name="recents_empty_message" msgid="7040467240571714191">"சமீபத்தியவை எதுவுமில்லை"</string>
-    <string name="accessibility_close_task" msgid="5354563209433803643">"மூடும்"</string>
-    <string name="recents_clear_all" msgid="5328176793634888831">"எல்லாம் அழி"</string>
-</resources>
diff --git a/quickstep/res/values-ta/strings.xml b/quickstep/res/values-ta/strings.xml
index 19518e1..97d51cd 100644
--- a/quickstep/res/values-ta/strings.xml
+++ b/quickstep/res/values-ta/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"மேலோட்டப் பார்வை"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"சமீபத்தியவை எதுவுமில்லை"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"மூடும்"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ஆப்ஸ் உபயோக அமைப்புகள்"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"எல்லாம் அழி"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"சமீபத்திய ஆப்ஸ்"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 நி"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"இன்று <xliff:g id="TIME">%1$s</xliff:g> மீதமுள்ளது"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"ஆப்ஸ் பரிந்துரைகள்"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"அனைத்து ஆப்ஸும்"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"நீங்கள் கணித்த ஆப்ஸ்"</string>
 </resources>
diff --git a/quickstep/res/values-te-rIN/strings.xml b/quickstep/res/values-te-rIN/strings.xml
deleted file mode 100644
index 416fbb8..0000000
--- a/quickstep/res/values-te-rIN/strings.xml
+++ /dev/null
@@ -1,28 +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 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="accessibility_desc_recent_apps" msgid="1444379410873162882">"అవలోకనం"</string>
-    <string name="recents_empty_message" msgid="7040467240571714191">"ఇటీవలి అంశాలు ఏవీ లేవు"</string>
-    <string name="accessibility_close_task" msgid="5354563209433803643">"మూసివేయండి"</string>
-    <string name="recents_clear_all" msgid="5328176793634888831">"అన్నీ తీసివేయండి"</string>
-</resources>
diff --git a/quickstep/res/values-te/strings.xml b/quickstep/res/values-te/strings.xml
index 4c0e5ac..24b37f7 100644
--- a/quickstep/res/values-te/strings.xml
+++ b/quickstep/res/values-te/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"అవలోకనం"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"ఇటీవలి అంశాలు ఏవీ లేవు"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"మూసివేయండి"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"యాప్ వినియోగ సెట్టింగ్‌లు"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"అన్నీ తీసివేయండి"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"ఇటీవలి యాప్‌లు"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 నిమిషం"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"నేటికి <xliff:g id="TIME">%1$s</xliff:g> మిగిలి ఉంది"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"యాప్ సూచనలు"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"అన్ని యాప్‌లు"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"మీ సూచించబడిన యాప్‌లు"</string>
 </resources>
diff --git a/quickstep/res/values-th/strings.xml b/quickstep/res/values-th/strings.xml
index 8dfda24..0f6821b 100644
--- a/quickstep/res/values-th/strings.xml
+++ b/quickstep/res/values-th/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"ภาพรวม"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"ไม่มีรายการล่าสุด"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"ปิด"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"การตั้งค่าการใช้แอป"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"ล้างทั้งหมด"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"แอปล่าสุด"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt;1 นาที"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"วันนี้เหลืออีก <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"คำแนะนำเกี่ยวกับแอป"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"แอปทั้งหมด"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"แอปที่คาดการณ์ไว้"</string>
 </resources>
diff --git a/quickstep/res/values-tl/strings.xml b/quickstep/res/values-tl/strings.xml
index ab3cac9..491bac5 100644
--- a/quickstep/res/values-tl/strings.xml
+++ b/quickstep/res/values-tl/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Overview"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Walang kamakailang item"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Isara"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Mga setting ng paggamit ng app"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"I-clear lahat"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Mga kamakailang app"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> na lang ngayon"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Mga iminumungkahing app"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Lahat ng app"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Iyong mga nahulaang app"</string>
 </resources>
diff --git a/quickstep/res/values-tr/strings.xml b/quickstep/res/values-tr/strings.xml
index 9693413..ec6d884 100644
--- a/quickstep/res/values-tr/strings.xml
+++ b/quickstep/res/values-tr/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Genel bakış"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Yeni öğe yok"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Kapat"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Uygulama kullanım ayarları"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Tümünü temizle"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Son uygulamalar"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 dk."</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Bugün <xliff:g id="TIME">%1$s</xliff:g> kaldı"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Uygulama önerileri"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Tüm uygulamalar"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Tahmin edilen uygulamalarınız"</string>
 </resources>
diff --git a/quickstep/res/values-uk/strings.xml b/quickstep/res/values-uk/strings.xml
index 2afcb31..7736062 100644
--- a/quickstep/res/values-uk/strings.xml
+++ b/quickstep/res/values-uk/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Огляд"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Немає нещодавніх додатків"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Закрити"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Налаштування використання додатка"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Очистити все"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Нещодавні додатки"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 хв"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Сьогодні залишилося <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Пропозиції додатків"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Усі додатки"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Передбачені додатки"</string>
 </resources>
diff --git a/quickstep/res/values-ur-rPK/strings.xml b/quickstep/res/values-ur-rPK/strings.xml
deleted file mode 100644
index 0a546fd..0000000
--- a/quickstep/res/values-ur-rPK/strings.xml
+++ /dev/null
@@ -1,28 +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 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="accessibility_desc_recent_apps" msgid="1444379410873162882">"مجموعی جائزہ"</string>
-    <string name="recents_empty_message" msgid="7040467240571714191">"کوئی حالیہ آئٹم نہیں"</string>
-    <string name="accessibility_close_task" msgid="5354563209433803643">"بند کریں"</string>
-    <string name="recents_clear_all" msgid="5328176793634888831">"سبھی کو صاف کریں"</string>
-</resources>
diff --git a/quickstep/res/values-ur/strings.xml b/quickstep/res/values-ur/strings.xml
index f493a2f..87b303f 100644
--- a/quickstep/res/values-ur/strings.xml
+++ b/quickstep/res/values-ur/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"مجموعی جائزہ"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"کوئی حالیہ آئٹم نہیں"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"بند کریں"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ایپ کے استعمال کی ترتیبات"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"سبھی کو صاف کریں"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"حالیہ ایپس"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>،<xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"‏&lt; 1 منٹ"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"آج <xliff:g id="TIME">%1$s</xliff:g> بچا ہے"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"ایپ کی تجاویز"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"تمام ایپس"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"آپ کی پیشن گوئی کردہ ایپس"</string>
 </resources>
diff --git a/quickstep/res/values-uz-rUZ/strings.xml b/quickstep/res/values-uz-rUZ/strings.xml
deleted file mode 100644
index efea341..0000000
--- a/quickstep/res/values-uz-rUZ/strings.xml
+++ /dev/null
@@ -1,28 +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 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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Nazar"</string>
-    <string name="recents_empty_message" msgid="7040467240571714191">"Yaqinda ishlatilgan ilovalar yo‘q"</string>
-    <string name="accessibility_close_task" msgid="5354563209433803643">"Yopish"</string>
-    <string name="recents_clear_all" msgid="5328176793634888831">"Hammasini tozalash"</string>
-</resources>
diff --git a/quickstep/res/values-uz/strings.xml b/quickstep/res/values-uz/strings.xml
index 4911925..67c8e91 100644
--- a/quickstep/res/values-uz/strings.xml
+++ b/quickstep/res/values-uz/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Nazar"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Yaqinda ishlatilgan ilovalar yo‘q"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Yopish"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Ilovadan foydalanish sozlamalari"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Hammasini tozalash"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Yaqinda ishlatilgan ilovalar"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 daqiqa"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Bugun <xliff:g id="TIME">%1$s</xliff:g> qoldi"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Tavsiya etiladigan ilovalar"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Barcha ilovalar"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Taklif qilingan ilovalaringiz"</string>
 </resources>
diff --git a/quickstep/res/values-vi/strings.xml b/quickstep/res/values-vi/strings.xml
index 406faf5..34c89ef 100644
--- a/quickstep/res/values-vi/strings.xml
+++ b/quickstep/res/values-vi/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Tổng quan"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Không có mục gần đây nào"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Đóng"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Cài đặt mức sử dụng ứng dụng"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Xóa tất cả"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Ứng dụng gần đây"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 phút"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"Hôm nay còn <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Các ứng dụng đề xuất"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Tất cả ứng dụng"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Các ứng dụng gợi ý của bạn"</string>
 </resources>
diff --git a/quickstep/res/values-zh-rCN/strings.xml b/quickstep/res/values-zh-rCN/strings.xml
index 71ac114..0e83977 100644
--- a/quickstep/res/values-zh-rCN/strings.xml
+++ b/quickstep/res/values-zh-rCN/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"概览"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"近期没有任何内容"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"关闭"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"应用使用设置"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"全部清除"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"最近用过的应用"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>(<xliff:g id="REMAINING_TIME">%2$s</xliff:g>)"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"不到 1 分钟"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"今天还可使用 <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"应用推荐"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"所有应用"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"您的预测应用"</string>
 </resources>
diff --git a/quickstep/res/values-zh-rHK/strings.xml b/quickstep/res/values-zh-rHK/strings.xml
index ab29a91..ac7e8e9 100644
--- a/quickstep/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-zh-rHK/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"概覽"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"最近沒有任何項目"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"關閉"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"應用程式使用情況設定"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"全部清除"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"最近使用的應用程式"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>,<xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"少於 1 分鐘"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"今天剩餘時間:<xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"應用程式建議"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"所有應用程式"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"您的預測應用程式"</string>
 </resources>
diff --git a/quickstep/res/values-zh-rTW/strings.xml b/quickstep/res/values-zh-rTW/strings.xml
index 1a9448c..3323bfd 100644
--- a/quickstep/res/values-zh-rTW/strings.xml
+++ b/quickstep/res/values-zh-rTW/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"總覽"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"最近沒有任何項目"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"關閉"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"應用程式使用情況設定"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"全部清除"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"最近使用的應用程式"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> (<xliff:g id="REMAINING_TIME">%2$s</xliff:g>)"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 分鐘"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"今天還能使用 <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"應用程式建議"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"所有應用程式"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"系統預測你會使用的應用程式"</string>
 </resources>
diff --git a/quickstep/res/values-zu/strings.xml b/quickstep/res/values-zu/strings.xml
index 3d4f372..0f1d99d 100644
--- a/quickstep/res/values-zu/strings.xml
+++ b/quickstep/res/values-zu/strings.xml
@@ -21,9 +21,17 @@
     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="accessibility_desc_recent_apps" msgid="1444379410873162882">"Buka konke"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Azikho izinto zakamuva"</string>
     <string name="accessibility_close_task" msgid="5354563209433803643">"Vala"</string>
+    <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Izilungiselelo zokusetshenziswa kohlelo lokusebenza"</string>
     <string name="recents_clear_all" msgid="5328176793634888831">"Sula konke"</string>
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Izinhlelo zokusebenza zakamuva"</string>
+    <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
+    <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 iminithi"</string>
+    <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> esele namhlanje"</string>
+    <string name="title_app_suggestions" msgid="4185902664111965088">"Iziphakamiso zohlelo lokusebenza"</string>
+    <string name="all_apps_label" msgid="8542784161730910663">"Zonke izinhlelo zokusebenza"</string>
+    <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Izinhlelo zakho zokusebenza eziqagelwe"</string>
 </resources>
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index c294376..5c4d6d8 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -18,5 +18,21 @@
 
     <string name="overview_callbacks_class" translatable="false"></string>
 
+    <!-- Activity which blocks home gesture -->
+    <string name="gesture_blocking_activity" translatable="false"></string>
+
     <string name="user_event_dispatcher_class" translatable="false">com.android.quickstep.logging.UserEventDispatcherExtension</string>
+
+    <string name="stats_log_manager_class" translatable="false">com.android.quickstep.logging.StatsLogCompatManager</string>
+
+    <string name="test_information_handler_class" translatable="false">com.android.quickstep.QuickstepTestInformationHandler</string>
+
+    <!-- The number of thumbnails and icons to keep in the cache. The thumbnail cache size also
+         determines how many thumbnails will be fetched in the background. -->
+    <integer name="recentsThumbnailCacheSize">3</integer>
+    <integer name="recentsIconCacheSize">12</integer>
+
+    <!-- Assistant Gesture -->
+    <integer name="assistant_gesture_min_time_threshold">200</integer>
+    <integer name="assistant_gesture_corner_deg_threshold">20</integer>
 </resources>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 49c4492..b0968f9 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -19,18 +19,28 @@
     <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_corner_radius">2dp</dimen>
+    <!-- For screens without rounded corners -->
+    <dimen name="task_corner_radius_small">2dp</dimen>
+
     <dimen name="recents_page_spacing">10dp</dimen>
     <dimen name="recents_clear_all_deadzone_vertical_margin">70dp</dimen>
-    <dimen name="quickscrub_adjacent_visible_width">20dp</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>
+
     <!-- Launcher app transition -->
     <dimen name="content_trans_y">50dp</dimen>
     <dimen name="workspace_trans_y">50dp</dimen>
@@ -52,7 +62,14 @@
        docked_stack_divider_thickness - 2 * docked_stack_divider_insets -->
     <dimen name="multi_window_task_divider_size">10dp</dimen>
 
-    <dimen name="shelf_surface_radius">16dp</dimen>
     <!-- same as vertical_drag_handle_size -->
     <dimen name="shelf_surface_offset">24dp</dimen>
+
+    <!-- Assistant Gestures -->
+    <!-- Distance from the vertical edges of the screen in which assist gestures are recognized -->
+    <dimen name="gestures_assistant_width">48dp</dimen>
+    <dimen name="gestures_assistant_drag_threshold">55dp</dimen>
+
+    <!-- Distance to move elements when swiping up to go home from launcher -->
+    <dimen name="home_pullback_distance">28dp</dimen>
 </resources>
diff --git a/quickstep/res/values/override.xml b/quickstep/res/values/override.xml
deleted file mode 100644
index d683659..0000000
--- a/quickstep/res/values/override.xml
+++ /dev/null
@@ -1,24 +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 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="main_process_initializer_class" translatable="false">com.android.quickstep.QuickstepProcessInitializer</string>
-</resources>
-
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index a76899d..4319b5d 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -16,7 +16,7 @@
 * limitations under the License.
 */
 -->
-<resources>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
 
     <!-- Application name -->
     <string name="derived_app_name" translatable="false">Quickstep</string>
@@ -26,6 +26,8 @@
     <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 -->
+    <string name="recent_task_option_freeform">Freeform</string>
 
     <!-- Content description for the recent apps panel (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_desc_recent_apps">Overview</string>
@@ -36,9 +38,33 @@
     <!-- Content description for the recent apps's accessibility option that closes it. [CHAR LIMIT=NONE] -->
     <string name="accessibility_close_task">Close</string>
 
+    <!-- Content description for the recent apps's accessibility option that opens its usage settings. [CHAR LIMIT=NONE] -->
+    <string name="accessibility_app_usage_settings">App usage settings</string>
+
     <!-- Recents: Title of a button that clears the task list, i.e. closes all tasks. [CHAR LIMIT=30] -->
     <string name="recents_clear_all">Clear all</string>
 
     <!-- Accessibility title for the list of recent apps [CHAR_LIMIT=none] -->
     <string name="accessibility_recent_apps">Recent apps</string>
+
+    <!-- Accessibility title for an app card in Recents for apps that have time limit set
+     [CHAR_LIMIT=none] -->
+    <string name="task_contents_description_with_remaining_time"><xliff:g id="task_description" example="GMail">%1$s</xliff:g>, <xliff:g id="remaining_time" example="7 minutes left today">%2$s</xliff:g></string>
+
+    <!-- Text to show total app usage per day if it is less than 1 minute ("&lt;" is the
+     escaped form of '<'). [CHAR LIMIT=15] -->
+    <string name="shorter_duration_less_than_one_minute">&lt; 1 minute</string>
+
+    <!-- Annotation shown on an app card in Recents, telling that the app has a usage limit set by
+    the user, and a given time is left for it today [CHAR LIMIT=22] -->
+    <string name="time_left_for_app"><xliff:g id="time" example="7 minutes">%1$s</xliff:g> left today</string>
+
+    <!-- Accessibility title for the row of all-apps containing app predictions. [CHAR LIMIT=50] -->
+    <string name="title_app_suggestions">App suggestions</string>
+    <!-- Label for the header text of the All Apps section in All Apps view, used to separate Predicted Apps and Actions section from All Apps section. [CHAR_LIMIT=50] -->
+    <string name="all_apps_label">All apps</string>
+    <!-- Text of the tip when user lands in all apps view for the first time, indicating where the tip toast points to is the predicted apps section. [CHAR_LIMIT=50] -->
+    <string name="all_apps_prediction_tip">Your predicted apps</string>
+
+
 </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 30f3100..c7e864f 100644
--- a/quickstep/res/xml/indexable_launcher_prefs.xml
+++ b/quickstep/res/xml/indexable_launcher_prefs.xml
@@ -29,13 +29,4 @@
         android:defaultValue="@bool/allow_rotation"
         android:persistent="true" />
 
-    <ListPreference
-        android:key="pref_override_icon_shape"
-        android:title="@string/icon_shape_override_label"
-        android:summary="@string/icon_shape_override_label_location"
-        android:entries="@array/icon_shape_override_paths_names"
-        android:entryValues="@array/icon_shape_override_paths_values"
-        android:defaultValue=""
-        android:persistent="false" />
-
 </PreferenceScreen>
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
index e346310..78f6ffa 100644
--- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -17,7 +17,8 @@
 
 import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
 import static com.android.launcher3.Utilities.postAsyncCallback;
-import static com.android.systemui.shared.recents.utilities.Utilities.postAtFrontOfQueueAsynchronously;
+import static com.android.systemui.shared.recents.utilities.Utilities
+        .postAtFrontOfQueueAsynchronously;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -25,12 +26,13 @@
 import android.annotation.TargetApi;
 import android.os.Build;
 import android.os.Handler;
-import android.support.annotation.BinderThread;
-import android.support.annotation.UiThread;
 
 import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
+import androidx.annotation.BinderThread;
+import androidx.annotation.UiThread;
+
 @TargetApi(Build.VERSION_CODES.P)
 public abstract class LauncherAnimationRunner implements RemoteAnimationRunnerCompat {
 
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
deleted file mode 100644
index 14633af..0000000
--- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ /dev/null
@@ -1,816 +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.LauncherAnimUtils.SCALE_PROPERTY;
-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.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.LINEAR;
-import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_TRANSITIONS;
-import static com.android.quickstep.TaskUtils.findTaskViewToLaunch;
-import static com.android.quickstep.TaskUtils.getRecentsWindowAnimator;
-import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
-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.Rect;
-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 android.view.ViewGroup;
-
-import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
-import com.android.launcher3.InsettableFrameLayout.LayoutParams;
-import com.android.launcher3.allapps.AllAppsTransitionController;
-import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.dragndrop.DragLayer;
-import com.android.launcher3.graphics.DrawableFactory;
-import com.android.launcher3.shortcuts.DeepShortcutView;
-import com.android.launcher3.util.MultiValueAlpha;
-import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
-import com.android.quickstep.util.ClipAnimationHelper;
-import com.android.quickstep.util.MultiValueUpdateListener;
-import com.android.quickstep.util.RemoteAnimationProvider;
-import com.android.quickstep.util.RemoteAnimationTargetSet;
-import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.system.ActivityCompat;
-import com.android.systemui.shared.system.ActivityOptionsCompat;
-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.SyncRtSurfaceTransactionApplier;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier.SurfaceParams;
-import com.android.systemui.shared.system.WindowManagerWrapper;
-
-/**
- * Manages the opening and closing app transitions from Launcher.
- */
-@TargetApi(Build.VERSION_CODES.O)
-@SuppressWarnings("unused")
-public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManager
-        implements OnDeviceProfileChangeListener {
-
-    private static final String TAG = "LauncherTransition";
-
-    /** 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 int APP_LAUNCH_DURATION = 500;
-    // Use a shorter duration for x or y translation to create a curve effect
-    private static final int APP_LAUNCH_CURVED_DURATION = APP_LAUNCH_DURATION / 2;
-    // 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 int APP_LAUNCH_ALPHA_START_DELAY = 32;
-    private static final int APP_LAUNCH_ALPHA_DURATION = 50;
-
-    public static final int RECENTS_LAUNCH_DURATION = 336;
-    public static final int RECENTS_QUICKSCRUB_LAUNCH_DURATION = 300;
-    private static final int LAUNCHER_RESUME_START_DELAY = 100;
-    private static final int CLOSING_TRANSITION_DURATION_MS = 250;
-
-    // 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;
-
-    private final Launcher mLauncher;
-    private final DragLayer mDragLayer;
-    private final AlphaProperty mDragLayerAlpha;
-
-    private final Handler mHandler;
-    private final boolean mIsRtl;
-
-    private final float mContentTransY;
-    private final float mWorkspaceTransY;
-    private final float mClosingWindowTransY;
-
-    private DeviceProfile mDeviceProfile;
-    private View mFloatingView;
-
-    private RemoteAnimationProvider mRemoteAnimationProvider;
-
-    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 LauncherAppTransitionManagerImpl(Context context) {
-        mLauncher = 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);
-        registerRemoteAnimations();
-    }
-
-    @Override
-    public void onDeviceProfileChanged(DeviceProfile dp) {
-        mDeviceProfile = dp;
-    }
-
-    /**
-     * @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()) {
-            RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(mHandler,
-                    true /* startAtFrontOfQueue */) {
-
-                @Override
-                public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
-                        AnimationResult result) {
-                    AnimatorSet anim = new AnimatorSet();
-
-                    boolean launcherClosing =
-                            launcherIsATargetWithMode(targetCompats, MODE_CLOSING);
-
-                    if (!composeRecentsLaunchAnimator(v, targetCompats, anim)) {
-                        // Set the state animation first so that any state listeners are called
-                        // before our internal listeners.
-                        mLauncher.getStateManager().setCurrentAnimation(anim);
-
-                        Rect windowTargetBounds = getWindowTargetBounds(targetCompats);
-                        playIconAnimators(anim, v, windowTargetBounds);
-                        if (launcherClosing) {
-                            Pair<AnimatorSet, Runnable> launcherContentAnimator =
-                                    getLauncherContentAnimator(true /* isAppOpening */);
-                            anim.play(launcherContentAnimator.first);
-                            anim.addListener(new AnimatorListenerAdapter() {
-                                @Override
-                                public void onAnimationEnd(Animator animation) {
-                                    launcherContentAnimator.second.run();
-                                }
-                            });
-                        }
-                        anim.play(getOpeningWindowAnimators(v, targetCompats, windowTargetBounds));
-                    }
-
-                    if (launcherClosing) {
-                        anim.addListener(mForceInvisibleListener);
-                    }
-
-                    result.setAnimation(anim);
-                }
-            };
-
-            boolean fromRecents = mLauncher.getStateManager().getState().overviewUi
-                    && findTaskViewToLaunch(launcher, v, null) != null;
-            int duration = fromRecents
-                    ? RECENTS_LAUNCH_DURATION
-                    : APP_LAUNCH_DURATION;
-
-            int statusBarTransitionDelay = duration - STATUS_BAR_TRANSITION_DURATION
-                    - STATUS_BAR_TRANSITION_PRE_DELAY;
-            return ActivityOptionsCompat.makeRemoteAnimation(new RemoteAnimationAdapterCompat(
-                    runner, duration, statusBarTransitionDelay));
-        }
-        return super.getActivityLaunchOptions(launcher, v);
-    }
-
-    /**
-     * 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[] targets) {
-        Rect bounds = new Rect(0, 0, mDeviceProfile.widthPx, mDeviceProfile.heightPx);
-        if (mLauncher.isInMultiWindowModeCompat()) {
-            for (RemoteAnimationTargetCompat target : targets) {
-                if (target.mode == MODE_OPENING) {
-                    bounds.set(target.sourceContainerBounds);
-                    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;
-            }
-        });
-    }
-
-    /**
-     * Composes the animations for a launch from the recents list if possible.
-     */
-    private boolean composeRecentsLaunchAnimator(View v,
-            RemoteAnimationTargetCompat[] targets, AnimatorSet target) {
-        // Ensure recents is actually visible
-        if (!mLauncher.getStateManager().getState().overviewUi) {
-            return false;
-        }
-
-        RecentsView recentsView = mLauncher.getOverviewPanel();
-        boolean launcherClosing = launcherIsATargetWithMode(targets, MODE_CLOSING);
-        boolean skipLauncherChanges = !launcherClosing;
-        boolean isLaunchingFromQuickscrub =
-                recentsView.getQuickScrubController().isWaitingForTaskLaunch();
-
-        TaskView taskView = findTaskViewToLaunch(mLauncher, v, targets);
-        if (taskView == null) {
-            return false;
-        }
-
-        int duration = isLaunchingFromQuickscrub
-                ? RECENTS_QUICKSCRUB_LAUNCH_DURATION
-                : RECENTS_LAUNCH_DURATION;
-
-        ClipAnimationHelper helper = new ClipAnimationHelper();
-        target.play(getRecentsWindowAnimator(taskView, skipLauncherChanges, targets, helper)
-                .setDuration(duration));
-
-        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, helper);
-            launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
-            launcherAnim.setDuration(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, duration);
-            controller.dispatchOnStart();
-            childStateAnimation = controller.getTarget();
-            launcherAnim = controller.getAnimationPlayer().setDuration(duration);
-            windowAnimEndListener = new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mLauncher.getStateManager().goToState(NORMAL, false);
-                }
-            };
-        }
-        target.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(target, childStateAnimation);
-        target.addListener(windowAnimEndListener);
-        return true;
-    }
-
-    /**
-     * 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.
-     */
-    private Pair<AnimatorSet, Runnable> getLauncherContentAnimator(boolean isAppOpening) {
-        AnimatorSet launcherAnimator = new AnimatorSet();
-        Runnable endListener;
-
-        float[] alphas = isAppOpening
-                ? new float[] {1, 0}
-                : new float[] {0, 1};
-        float[] trans = isAppOpening
-                ? new float[] {0, mContentTransY}
-                : new float[] {-mContentTransY, 0};
-
-        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(217);
-            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(350);
-
-            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));
-
-            RecentsView overview = mLauncher.getOverviewPanel();
-            ObjectAnimator alpha = ObjectAnimator.ofFloat(overview,
-                    RecentsView.CONTENT_ALPHA, alphas);
-            alpha.setDuration(217);
-            alpha.setInterpolator(LINEAR);
-            launcherAnimator.play(alpha);
-
-            ObjectAnimator transY = ObjectAnimator.ofFloat(overview, View.TRANSLATION_Y, trans);
-            transY.setInterpolator(AGGRESSIVE_EASE);
-            transY.setDuration(350);
-            launcherAnimator.play(transY);
-
-            endListener = mLauncher.getStateManager()::reapplyState;
-        } else {
-            mDragLayerAlpha.setValue(alphas[0]);
-            ObjectAnimator alpha =
-                    ObjectAnimator.ofFloat(mDragLayerAlpha, MultiValueAlpha.VALUE, alphas);
-            alpha.setDuration(217);
-            alpha.setInterpolator(LINEAR);
-            launcherAnimator.play(alpha);
-
-            mDragLayer.setTranslationY(trans[0]);
-            ObjectAnimator transY = ObjectAnimator.ofFloat(mDragLayer, View.TRANSLATION_Y, trans);
-            transY.setInterpolator(AGGRESSIVE_EASE);
-            transY.setDuration(350);
-            launcherAnimator.play(transY);
-
-            mDragLayer.getScrim().hideSysUiScrim(true);
-            // Pause page indicator animations as they lead to layer trashing.
-            mLauncher.getWorkspace().getPageIndicator().pauseAnimations();
-            mDragLayer.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-
-            endListener = this::resetContentView;
-        }
-        return new Pair<>(launcherAnimator, endListener);
-    }
-
-    /**
-     * Animators for the "floating view" of the view used to launch the target.
-     */
-    private void playIconAnimators(AnimatorSet appOpenAnimator, View v, Rect windowTargetBounds) {
-        final boolean isBubbleTextView = v instanceof BubbleTextView;
-        mFloatingView = new View(mLauncher);
-        if (isBubbleTextView && v.getTag() instanceof ItemInfoWithIcon ) {
-            // Create a copy of the app icon
-            mFloatingView.setBackground(
-                    DrawableFactory.get(mLauncher).newIcon((ItemInfoWithIcon) v.getTag()));
-        }
-
-        // Position the floating view exactly on top of the original
-        Rect rect = new Rect();
-        final boolean fromDeepShortcutView = v.getParent() instanceof DeepShortcutView;
-        if (fromDeepShortcutView) {
-            // Deep shortcut views have their icon drawn in a separate view.
-            DeepShortcutView view = (DeepShortcutView) v.getParent();
-            mDragLayer.getDescendantRectRelativeToSelf(view.getIconView(), rect);
-        } else {
-            mDragLayer.getDescendantRectRelativeToSelf(v, rect);
-        }
-        int viewLocationLeft = rect.left;
-        int viewLocationTop = rect.top;
-
-        float startScale = 1f;
-        if (isBubbleTextView && !fromDeepShortcutView) {
-            BubbleTextView btv = (BubbleTextView) v;
-            btv.getIconBounds(rect);
-            Drawable dr = btv.getIcon();
-            if (dr instanceof FastBitmapDrawable) {
-                startScale = ((FastBitmapDrawable) dr).getAnimatedScale();
-            }
-        } else {
-            rect.set(0, 0, rect.width(), rect.height());
-        }
-        viewLocationLeft += rect.left;
-        viewLocationTop += rect.top;
-        int viewLocationStart = mIsRtl
-                ? windowTargetBounds.width() - rect.right
-                : viewLocationLeft;
-        LayoutParams lp = new LayoutParams(rect.width(), rect.height());
-        lp.ignoreInsets = true;
-        lp.setMarginStart(viewLocationStart);
-        lp.topMargin = viewLocationTop;
-        mFloatingView.setLayoutParams(lp);
-
-        // Set the properties here already to make sure they'are available when running the first
-        // animation frame.
-        mFloatingView.setLeft(viewLocationLeft);
-        mFloatingView.setTop(viewLocationTop);
-        mFloatingView.setRight(viewLocationLeft + rect.width());
-        mFloatingView.setBottom(viewLocationTop + rect.height());
-
-        // Swap the two views in place.
-        ((ViewGroup) mDragLayer.getParent()).addView(mFloatingView);
-        v.setVisibility(View.INVISIBLE);
-
-        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 xPosition = mIsRtl
-                ? windowTargetBounds.width() - lp.getMarginStart() - rect.width()
-                : lp.getMarginStart();
-        float dX = centerX - xPosition - (lp.width / 2);
-        float dY = centerY - lp.topMargin - (lp.height / 2);
-
-        ObjectAnimator x = ObjectAnimator.ofFloat(mFloatingView, View.TRANSLATION_X, 0f, dX);
-        ObjectAnimator y = ObjectAnimator.ofFloat(mFloatingView, View.TRANSLATION_Y, 0f, dY);
-
-        // Use upward animation for apps that are either on the bottom half of the screen, or are
-        // relatively close to the center.
-        boolean useUpwardAnimation = lp.topMargin > centerY
-                || Math.abs(dY) < mLauncher.getDeviceProfile().cellHeightPx;
-        if (useUpwardAnimation) {
-            x.setDuration(APP_LAUNCH_CURVED_DURATION);
-            y.setDuration(APP_LAUNCH_DURATION);
-        } else {
-            x.setDuration((long) (APP_LAUNCH_DOWN_DUR_SCALE_FACTOR * APP_LAUNCH_DURATION));
-            y.setDuration((long) (APP_LAUNCH_DOWN_DUR_SCALE_FACTOR * APP_LAUNCH_CURVED_DURATION));
-        }
-        x.setInterpolator(AGGRESSIVE_EASE);
-        y.setInterpolator(AGGRESSIVE_EASE);
-        appOpenAnimator.play(x);
-        appOpenAnimator.play(y);
-
-        // Scale the app icon to take up the entire screen. This simplifies the math when
-        // animating the app window position / scale.
-        float maxScaleX = windowTargetBounds.width() / (float) rect.width();
-        float maxScaleY = windowTargetBounds.height() / (float) rect.height();
-        float scale = Math.max(maxScaleX, maxScaleY);
-        ObjectAnimator scaleAnim = ObjectAnimator
-                .ofFloat(mFloatingView, SCALE_PROPERTY, startScale, scale);
-        scaleAnim.setDuration(APP_LAUNCH_DURATION)
-                .setInterpolator(Interpolators.EXAGGERATED_EASE);
-        appOpenAnimator.play(scaleAnim);
-
-        // Fade out the app icon.
-        ObjectAnimator alpha = ObjectAnimator.ofFloat(mFloatingView, View.ALPHA, 1f, 0f);
-        if (useUpwardAnimation) {
-            alpha.setStartDelay(APP_LAUNCH_ALPHA_START_DELAY);
-            alpha.setDuration(APP_LAUNCH_ALPHA_DURATION);
-        } else {
-            alpha.setStartDelay((long) (APP_LAUNCH_DOWN_DUR_SCALE_FACTOR
-                    * APP_LAUNCH_ALPHA_START_DELAY));
-            alpha.setDuration((long) (APP_LAUNCH_DOWN_DUR_SCALE_FACTOR * APP_LAUNCH_ALPHA_DURATION));
-        }
-        alpha.setInterpolator(LINEAR);
-        appOpenAnimator.play(alpha);
-
-        appOpenAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                // Reset launcher to normal state
-                v.setVisibility(View.VISIBLE);
-                ((ViewGroup) mDragLayer.getParent()).removeView(mFloatingView);
-            }
-        });
-    }
-
-    /**
-     * @return Animator that controls the window of the opening targets.
-     */
-    private ValueAnimator getOpeningWindowAnimators(View v, RemoteAnimationTargetCompat[] targets,
-            Rect windowTargetBounds) {
-        Rect bounds = new Rect();
-        if (v.getParent() instanceof DeepShortcutView) {
-            // Deep shortcut views have their icon drawn in a separate view.
-            DeepShortcutView view = (DeepShortcutView) v.getParent();
-            mDragLayer.getDescendantRectRelativeToSelf(view.getIconView(), bounds);
-        } else if (v instanceof BubbleTextView) {
-            ((BubbleTextView) v).getIconBounds(bounds);
-        } else {
-            mDragLayer.getDescendantRectRelativeToSelf(v, bounds);
-        }
-        int[] floatingViewBounds = new int[2];
-
-        Rect crop = new Rect();
-        Matrix matrix = new Matrix();
-
-        RemoteAnimationTargetSet openingTargets = new RemoteAnimationTargetSet(targets,
-                MODE_OPENING);
-        RemoteAnimationTargetSet closingTargets = new RemoteAnimationTargetSet(targets,
-                MODE_CLOSING);
-        SyncRtSurfaceTransactionApplier surfaceApplier = new SyncRtSurfaceTransactionApplier(
-                mFloatingView);
-
-        ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
-        appAnimator.setDuration(APP_LAUNCH_DURATION);
-        appAnimator.addUpdateListener(new MultiValueUpdateListener() {
-            // Fade alpha for the app window.
-            FloatProp mAlpha = new FloatProp(0f, 1f, 0, 60, LINEAR);
-
-            @Override
-            public void onUpdate(float percent) {
-                final float easePercent = AGGRESSIVE_EASE.getInterpolation(percent);
-
-                // Calculate app icon size.
-                float iconWidth = bounds.width() * mFloatingView.getScaleX();
-                float iconHeight = bounds.height() * mFloatingView.getScaleY();
-
-                // Scale the app window to match the icon size.
-                float scaleX = iconWidth / windowTargetBounds.width();
-                float scaleY = iconHeight / windowTargetBounds.height();
-                float scale = Math.min(1f, Math.min(scaleX, scaleY));
-
-                // Position the scaled window on top of the icon
-                int windowWidth = windowTargetBounds.width();
-                int windowHeight = windowTargetBounds.height();
-                float scaledWindowWidth = windowWidth * scale;
-                float scaledWindowHeight = windowHeight * scale;
-
-                float offsetX = (scaledWindowWidth - iconWidth) / 2;
-                float offsetY = (scaledWindowHeight - iconHeight) / 2;
-                mFloatingView.getLocationOnScreen(floatingViewBounds);
-
-                float transX0 = floatingViewBounds[0] - offsetX;
-                float transY0 = floatingViewBounds[1] - offsetY;
-
-                // Animate the window crop so that it starts off as a square, and then reveals
-                // horizontally.
-                float cropHeight = windowHeight * easePercent + windowWidth * (1 - easePercent);
-                float initialTop = (windowHeight - windowWidth) / 2f;
-                crop.left = 0;
-                crop.top = (int) (initialTop * (1 - easePercent));
-                crop.right = windowWidth;
-                crop.bottom = (int) (crop.top + cropHeight);
-
-                SurfaceParams[] params = new SurfaceParams[targets.length];
-                for (int i = targets.length - 1; i >= 0; i--) {
-                    RemoteAnimationTargetCompat target = targets[i];
-
-                    Rect targetCrop;
-                    float alpha;
-                    if (target.mode == MODE_OPENING) {
-                        matrix.setScale(scale, scale);
-                        matrix.postTranslate(transX0, transY0);
-                        targetCrop = crop;
-                        alpha = mAlpha.value;
-                    } else {
-                        matrix.setTranslate(target.position.x, target.position.y);
-                        alpha = 1f;
-                        targetCrop = target.sourceContainerBounds;
-                    }
-
-                    params[i] = new SurfaceParams(target.leash, alpha, matrix, targetCrop,
-                            RemoteAnimationProvider.getLayer(target, MODE_OPENING));
-                }
-                surfaceApplier.scheduleApply(params);
-            }
-        });
-        return appAnimator;
-    }
-
-    /**
-     * Registers remote animations used when closing apps to home screen.
-     */
-    private void registerRemoteAnimations() {
-        // Unregister this
-        if (hasControlRemoteAppTransitionPermission()) {
-            RemoteAnimationDefinitionCompat definition = new RemoteAnimationDefinitionCompat();
-            definition.addRemoteAnimation(WindowManagerWrapper.TRANSIT_WALLPAPER_OPEN,
-                    WindowManagerWrapper.ACTIVITY_TYPE_STANDARD,
-                    new RemoteAnimationAdapterCompat(getWallpaperOpenRunner(),
-                            CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
-
-            // TODO: Transition for unlock to home TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER
-            new ActivityCompat(mLauncher).registerRemoteAnimations(definition);
-        }
-    }
-
-    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.
-     */
-    private RemoteAnimationRunnerCompat getWallpaperOpenRunner() {
-        return new LauncherAnimationRunner(mHandler, false /* startAtFrontOfQueue */) {
-            @Override
-            public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
-                    AnimationResult result) {
-                if (!mLauncher.hasBeenResumed()) {
-                    // If launcher is not resumed, wait until new async-frame after resume
-                    mLauncher.setOnResumeCallback(() ->
-                            postAsyncCallback(mHandler, () ->
-                                    onCreateAnimation(targetCompats, 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(targetCompats);
-                }
-
-                if (anim == null) {
-                    anim = new AnimatorSet();
-                    anim.play(getClosingWindowAnimators(targetCompats));
-
-                    // 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(targetCompats, MODE_OPENING)
-                            || mLauncher.isForceInvisible()) {
-                        // Only register the content animation for cancellation when state changes
-                        mLauncher.getStateManager().setCurrentAnimation(anim);
-                        createLauncherResumeAnimation(anim);
-                    }
-                }
-
-                mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL);
-                result.setAnimation(anim);
-            }
-        };
-    }
-
-    /**
-     * Animator that controls the transformations of the windows the targets that are closing.
-     */
-    private Animator getClosingWindowAnimators(RemoteAnimationTargetCompat[] targets) {
-        SyncRtSurfaceTransactionApplier surfaceApplier =
-                new SyncRtSurfaceTransactionApplier(mDragLayer);
-        Matrix matrix = new Matrix();
-        ValueAnimator closingAnimator = ValueAnimator.ofFloat(0, 1);
-        int duration = CLOSING_TRANSITION_DURATION_MS;
-        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[targets.length];
-                for (int i = targets.length - 1; i >= 0; i--) {
-                    RemoteAnimationTargetCompat target = targets[i];
-                    float alpha;
-                    if (target.mode == MODE_CLOSING) {
-                        matrix.setScale(mScale.value, mScale.value,
-                                target.sourceContainerBounds.centerX(),
-                                target.sourceContainerBounds.centerY());
-                        matrix.postTranslate(0, mDy.value);
-                        matrix.postTranslate(target.position.x, target.position.y);
-                        alpha = mAlpha.value;
-                    } else {
-                        matrix.setTranslate(target.position.x, target.position.y);
-                        alpha = 1f;
-                    }
-                    params[i] = new SurfaceParams(target.leash, alpha, matrix,
-                            target.sourceContainerBounds,
-                            RemoteAnimationProvider.getLayer(target, MODE_CLOSING));
-                }
-                surfaceApplier.scheduleApply(params);
-            }
-        });
-
-        return closingAnimator;
-    }
-
-    /**
-     * Creates an animator that modifies Launcher as a result from {@link #getWallpaperOpenRunner}.
-     */
-    private void createLauncherResumeAnimation(AnimatorSet anim) {
-        if (mLauncher.isInState(LauncherState.ALL_APPS)) {
-            Pair<AnimatorSet, Runnable> contentAnimator =
-                    getLauncherContentAnimator(false /* isAppOpening */);
-            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 {
-            AnimatorSet workspaceAnimator = new AnimatorSet();
-
-            mDragLayer.setTranslationY(-mWorkspaceTransY);;
-            workspaceAnimator.play(ObjectAnimator.ofFloat(mDragLayer, View.TRANSLATION_Y,
-                    -mWorkspaceTransY, 0));
-
-            mDragLayerAlpha.setValue(0);
-            workspaceAnimator.play(ObjectAnimator.ofFloat(
-                    mDragLayerAlpha, MultiValueAlpha.VALUE, 0, 1f));
-
-            workspaceAnimator.setStartDelay(LAUNCHER_RESUME_START_DELAY);
-            workspaceAnimator.setDuration(333);
-            workspaceAnimator.setInterpolator(Interpolators.DEACCEL_1_7);
-
-            mDragLayer.getScrim().hideSysUiScrim(true);
-
-            // Pause page indicator animations as they lead to layer trashing.
-            mLauncher.getWorkspace().getPageIndicator().pauseAnimations();
-            mDragLayer.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-
-            workspaceAnimator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    resetContentView();
-                }
-            });
-            anim.play(workspaceAnimator);
-        }
-    }
-
-    private void resetContentView() {
-        mLauncher.getWorkspace().getPageIndicator().skipAnimationsToEnd();
-        mDragLayerAlpha.setValue(1f);
-        mDragLayer.setLayerType(View.LAYER_TYPE_NONE, null);
-        mDragLayer.setTranslationY(0f);
-        mDragLayer.getScrim().hideSysUiScrim(false);
-    }
-
-    private boolean hasControlRemoteAppTransitionPermission() {
-        return mLauncher.checkSelfPermission(CONTROL_REMOTE_APP_TRANSITION_PERMISSION)
-                == PackageManager.PERMISSION_GRANTED;
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/LauncherInitListener.java b/quickstep/src/com/android/launcher3/LauncherInitListener.java
index 08b6bfc..c5c5323 100644
--- a/quickstep/src/com/android/launcher3/LauncherInitListener.java
+++ b/quickstep/src/com/android/launcher3/LauncherInitListener.java
@@ -44,8 +44,8 @@
     @Override
     protected boolean init(Launcher launcher, boolean alreadyOnHome) {
         if (mRemoteAnimationProvider != null) {
-            LauncherAppTransitionManagerImpl appTransitionManager =
-                    (LauncherAppTransitionManagerImpl) launcher.getAppTransitionManager();
+            QuickstepAppTransitionManagerImpl appTransitionManager =
+                    (QuickstepAppTransitionManagerImpl) 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/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
new file mode 100644
index 0000000..b60a017
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -0,0 +1,828 @@
+/*
+ * 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.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.dragndrop.DragLayer.ALPHA_INDEX_TRANSITIONS;
+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.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 com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
+import com.android.launcher3.allapps.AllAppsTransitionController;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.shortcuts.DeepShortcutView;
+import com.android.launcher3.util.MultiValueAlpha;
+import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
+import com.android.launcher3.views.FloatingIconView;
+import com.android.quickstep.util.MultiValueUpdateListener;
+import com.android.quickstep.util.RemoteAnimationProvider;
+import com.android.quickstep.util.RemoteAnimationTargetSet;
+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;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
+import com.android.systemui.shared.system.WindowManagerWrapper;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * {@link LauncherAppTransitionManager} with Quickstep-specific app transitions for launching from
+ * home and/or all-apps.
+ */
+@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 Launcher 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;
+
+    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.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);
+        registerRemoteAnimations();
+    }
+
+    @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 */);
+            RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(mHandler,
+                    true /* startAtFrontOfQueue */) {
+
+                @Override
+                public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
+                        AnimationResult result) {
+                    AnimatorSet anim = new AnimatorSet();
+
+                    boolean launcherClosing =
+                            launcherIsATargetWithMode(targetCompats, MODE_CLOSING);
+
+                    if (isLaunchingFromRecents(v, targetCompats)) {
+                        composeRecentsLaunchAnimator(anim, v, targetCompats, launcherClosing);
+                    } else {
+                        composeIconLaunchAnimator(anim, v, targetCompats, launcherClosing);
+                    }
+
+                    if (launcherClosing) {
+                        anim.addListener(mForceInvisibleListener);
+                    }
+
+                    result.setAnimation(anim);
+                }
+            };
+
+            // 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 targets 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[] targets, 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 targets the list of opening/closing apps
+     * @param launcherClosing true if launcher is closing
+     */
+    private void composeIconLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
+            @NonNull RemoteAnimationTargetCompat[] targets, 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(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;
+        }
+        anim.play(getOpeningWindowAnimators(v, targets, 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();
+                }
+            });
+        }
+    }
+
+    /**
+     * 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[] targets) {
+        Rect bounds = new Rect(0, 0, mDeviceProfile.widthPx, mDeviceProfile.heightPx);
+        if (mLauncher.isInMultiWindowMode()) {
+            for (RemoteAnimationTargetCompat target : targets) {
+                if (target.mode == MODE_OPENING) {
+                    bounds.set(target.sourceContainerBounds);
+                    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);
+
+            mDragLayer.setTranslationY(trans[0]);
+            ObjectAnimator transY = ObjectAnimator.ofFloat(mDragLayer, View.TRANSLATION_Y, trans);
+            transY.setInterpolator(AGGRESSIVE_EASE);
+            transY.setDuration(CONTENT_TRANSLATION_DURATION);
+            launcherAnimator.play(transY);
+
+            mDragLayer.getScrim().hideSysUiScrim(true);
+            // Pause page indicator animations as they lead to layer trashing.
+            mLauncher.getWorkspace().getPageIndicator().pauseAnimations();
+            mDragLayer.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+
+            endListener = this::resetContentView;
+        }
+        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.
+     */
+    private ValueAnimator getOpeningWindowAnimators(View v, RemoteAnimationTargetCompat[] targets,
+            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();
+
+        RemoteAnimationTargetSet openingTargets = new RemoteAnimationTargetSet(targets,
+                MODE_OPENING);
+        SyncRtSurfaceTransactionApplierCompat surfaceApplier =
+                new SyncRtSurfaceTransactionApplierCompat(floatingView);
+        openingTargets.addDependentTransactionApplier(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 currentBounds = new RectF();
+        RectF temp = new RectF();
+
+        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 mIconScale = 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 app icon size.
+                float iconWidth = bounds.width() * mIconScale.value;
+                float iconHeight = bounds.height() * mIconScale.value;
+
+                // Animate the window crop so that it starts off as a square.
+                final int windowWidth;
+                final int windowHeight;
+                if (mDeviceProfile.isVerticalBarLayout()) {
+                    windowWidth = (int) mCroppedSize.value;
+                    windowHeight = windowTargetBounds.height();
+                } else {
+                    windowWidth = windowTargetBounds.width();
+                    windowHeight = (int) mCroppedSize.value;
+                }
+                crop.set(0, 0, windowWidth, windowHeight);
+
+                // Scale the app window to match the icon size.
+                float scaleX = iconWidth / windowWidth;
+                float scaleY = iconHeight / windowHeight;
+                float scale = Math.min(1f, Math.max(scaleX, scaleY));
+
+                float scaledWindowWidth = windowWidth * scale;
+                float scaledWindowHeight = windowHeight * scale;
+
+                float offsetX = (scaledWindowWidth - iconWidth) / 2;
+                float offsetY = (scaledWindowHeight - iconHeight) / 2;
+
+                // Calculate the window position
+                temp.set(bounds);
+                temp.offset(dragLayerBounds[0], dragLayerBounds[1]);
+                temp.offset(mDx.value, mDy.value);
+                Utilities.scaleRectFAboutCenter(temp, mIconScale.value);
+                float transX0 = temp.left - offsetX;
+                float transY0 = temp.top - offsetY;
+
+                float croppedHeight = (windowTargetBounds.height() - crop.height()) * scale;
+                float croppedWidth = (windowTargetBounds.width() - crop.width()) * scale;
+                SurfaceParams[] params = new SurfaceParams[targets.length];
+                for (int i = targets.length - 1; i >= 0; i--) {
+                    RemoteAnimationTargetCompat target = targets[i];
+                    Rect targetCrop;
+                    final float alpha;
+                    final float cornerRadius;
+                    if (target.mode == MODE_OPENING) {
+                        matrix.setScale(scale, scale);
+                        matrix.postTranslate(transX0, transY0);
+                        targetCrop = crop;
+                        alpha = 1f - mIconAlpha.value;
+                        cornerRadius = mWindowRadius.value;
+                        matrix.mapRect(currentBounds, targetBounds);
+                        if (mDeviceProfile.isVerticalBarLayout()) {
+                            currentBounds.right -= croppedWidth;
+                        } else {
+                            currentBounds.bottom -= croppedHeight;
+                        }
+                        floatingView.update(currentBounds, mIconAlpha.value, percent, 0f,
+                                cornerRadius * scale, true /* isOpening */);
+                    } else {
+                        matrix.setTranslate(target.position.x, target.position.y);
+                        targetCrop = target.sourceContainerBounds;
+                        alpha = 1f;
+                        cornerRadius = 0;
+                    }
+
+                    params[i] = new SurfaceParams(target.leash, alpha, matrix, targetCrop,
+                            RemoteAnimationProvider.getLayer(target, MODE_OPENING),
+                            cornerRadius);
+                }
+                surfaceApplier.scheduleApply(params);
+            }
+        });
+        return appAnimator;
+    }
+
+    /**
+     * Registers remote animations used when closing apps to home screen.
+     */
+    private void registerRemoteAnimations() {
+        // Unregister this
+        if (hasControlRemoteAppTransitionPermission()) {
+            RemoteAnimationDefinitionCompat definition = new RemoteAnimationDefinitionCompat();
+            definition.addRemoteAnimation(WindowManagerWrapper.TRANSIT_WALLPAPER_OPEN,
+                    WindowManagerWrapper.ACTIVITY_TYPE_STANDARD,
+                    new RemoteAnimationAdapterCompat(getWallpaperOpenRunner(false /* fromUnlock */),
+                            CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
+            new ActivityCompat(mLauncher).registerRemoteAnimations(definition);
+        }
+    }
+
+    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.
+     */
+    RemoteAnimationRunnerCompat getWallpaperOpenRunner(boolean fromUnlock) {
+        return new WallpaperOpenLauncherAnimationRunner(mHandler, false /* startAtFrontOfQueue */,
+                fromUnlock);
+    }
+
+    /**
+     * Animator that controls the transformations of the windows when unlocking the device.
+     */
+    private Animator getUnlockWindowAnimator(RemoteAnimationTargetCompat[] targets) {
+        SyncRtSurfaceTransactionApplierCompat surfaceApplier =
+                new SyncRtSurfaceTransactionApplierCompat(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[targets.length];
+                for (int i = targets.length - 1; i >= 0; i--) {
+                    RemoteAnimationTargetCompat target = targets[i];
+                    params[i] = new SurfaceParams(target.leash, 1f, null,
+                            target.sourceContainerBounds,
+                            RemoteAnimationProvider.getLayer(target, MODE_OPENING), cornerRadius);
+                }
+                surfaceApplier.scheduleApply(params);
+            }
+        });
+        return unlockAnimator;
+    }
+
+    /**
+     * Animator that controls the transformations of the windows the targets that are closing.
+     */
+    private Animator getClosingWindowAnimators(RemoteAnimationTargetCompat[] targets) {
+        SyncRtSurfaceTransactionApplierCompat surfaceApplier =
+                new SyncRtSurfaceTransactionApplierCompat(mDragLayer);
+        Matrix matrix = new Matrix();
+        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[targets.length];
+                for (int i = targets.length - 1; i >= 0; i--) {
+                    RemoteAnimationTargetCompat target = targets[i];
+                    final float alpha;
+                    final float cornerRadius;
+                    if (target.mode == MODE_CLOSING) {
+                        matrix.setScale(mScale.value, mScale.value,
+                                target.sourceContainerBounds.centerX(),
+                                target.sourceContainerBounds.centerY());
+                        matrix.postTranslate(0, mDy.value);
+                        matrix.postTranslate(target.position.x, target.position.y);
+                        alpha = mAlpha.value;
+                        cornerRadius = windowCornerRadius;
+                    } else {
+                        matrix.setTranslate(target.position.x, target.position.y);
+                        alpha = 1f;
+                        cornerRadius = 0f;
+                    }
+                    params[i] = new SurfaceParams(target.leash, alpha, matrix,
+                            target.sourceContainerBounds,
+                            RemoteAnimationProvider.getLayer(target, MODE_CLOSING),
+                            cornerRadius);
+                }
+                surfaceApplier.scheduleApply(params);
+            }
+        });
+
+        return closingAnimator;
+    }
+
+    /**
+     * Creates an animator that modifies Launcher as a result from {@link #getWallpaperOpenRunner}.
+     */
+    private void createLauncherResumeAnimation(AnimatorSet 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 {
+            AnimatorSet workspaceAnimator = new AnimatorSet();
+
+            mDragLayer.setTranslationY(-mWorkspaceTransY);;
+            workspaceAnimator.play(ObjectAnimator.ofFloat(mDragLayer, View.TRANSLATION_Y,
+                    -mWorkspaceTransY, 0));
+
+            mDragLayerAlpha.setValue(0);
+            workspaceAnimator.play(ObjectAnimator.ofFloat(
+                    mDragLayerAlpha, MultiValueAlpha.VALUE, 0, 1f));
+
+            workspaceAnimator.setStartDelay(LAUNCHER_RESUME_START_DELAY);
+            workspaceAnimator.setDuration(333);
+            workspaceAnimator.setInterpolator(Interpolators.DEACCEL_1_7);
+
+            mDragLayer.getScrim().hideSysUiScrim(true);
+
+            // Pause page indicator animations as they lead to layer trashing.
+            mLauncher.getWorkspace().getPageIndicator().pauseAnimations();
+            mDragLayer.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+
+            workspaceAnimator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    resetContentView();
+                }
+            });
+            anim.play(workspaceAnimator);
+        }
+    }
+
+    private void resetContentView() {
+        mLauncher.getWorkspace().getPageIndicator().skipAnimationsToEnd();
+        mDragLayerAlpha.setValue(1f);
+        mDragLayer.setLayerType(View.LAYER_TYPE_NONE, null);
+        mDragLayer.setTranslationY(0f);
+        mDragLayer.getScrim().hideSysUiScrim(false);
+    }
+
+    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.
+     */
+    class WallpaperOpenLauncherAnimationRunner extends LauncherAnimationRunner {
+        private final boolean mFromUnlock;
+
+        public WallpaperOpenLauncherAnimationRunner(Handler handler, boolean startAtFrontOfQueue,
+                boolean fromUnlock) {
+            super(handler, startAtFrontOfQueue);
+            mFromUnlock = fromUnlock;
+        }
+
+        @Override
+        public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
+                LauncherAnimationRunner.AnimationResult result) {
+            if (!mLauncher.hasBeenResumed()) {
+                // If launcher is not resumed, wait until new async-frame after resume
+                mLauncher.addOnResumeCallback(() ->
+                        postAsyncCallback(mHandler, () ->
+                                onCreateAnimation(targetCompats, 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(targetCompats);
+            }
+
+            if (anim == null) {
+                anim = new AnimatorSet();
+                anim.play(mFromUnlock
+                        ? getUnlockWindowAnimator(targetCompats)
+                        : getClosingWindowAnimators(targetCompats));
+
+                // 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(targetCompats, MODE_OPENING)
+                        || mLauncher.isForceInvisible()) {
+                    // Only register the content animation for cancellation when state changes
+                    mLauncher.getStateManager().setCurrentAnimation(anim);
+                    if (mFromUnlock) {
+                        Pair<AnimatorSet, Runnable> contentAnimator =
+                                getLauncherContentAnimator(false /* isAppOpening */,
+                                        new float[] {mContentTransY, 0});
+                        contentAnimator.first.setStartDelay(0);
+                        anim.play(contentAnimator.first);
+                        anim.addListener(new AnimatorListenerAdapter() {
+                            @Override
+                            public void onAnimationEnd(Animator animation) {
+                                contentAnimator.second.run();
+                            }
+                        });
+                    } else {
+                        createLauncherResumeAnimation(anim);
+                    }
+                }
+            }
+
+            mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL);
+            result.setAnimation(anim);
+        }
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java b/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java
new file mode 100644
index 0000000..e302b4f
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java
@@ -0,0 +1,82 @@
+/*
+ * 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.proxy;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender.SendIntentException;
+import android.os.Bundle;
+import android.util.Log;
+
+public class ProxyActivityStarter extends Activity {
+
+    private static final String TAG = "ProxyActivityStarter";
+
+    public static final String EXTRA_PARAMS = "start-activity-params";
+
+    private StartActivityParams mParams;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setVisible(false);
+
+        mParams = getIntent().getParcelableExtra(EXTRA_PARAMS);
+        if (mParams == null) {
+            Log.d(TAG, "Proxy activity started without params");
+            finishAndRemoveTask();
+            return;
+        }
+
+        if (savedInstanceState != null) {
+            // Already started the activity. Just wait for the result.
+            return;
+        }
+
+        if (mParams.intent != null) {
+            startActivityForResult(mParams.intent, mParams.requestCode, mParams.options);
+            return;
+        } else if (mParams.intentSender != null) {
+            try {
+                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);
+            }
+        }
+        finishAndRemoveTask();
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (requestCode == mParams.requestCode) {
+            mParams.deliverResult(this, resultCode, data);
+        }
+        finishAndRemoveTask();
+    }
+
+    public static Intent getLaunchIntent(Context context, StartActivityParams params) {
+        return new Intent(context, ProxyActivityStarter.class)
+                .putExtra(EXTRA_PARAMS, params)
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+                        | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/proxy/StartActivityParams.java b/quickstep/src/com/android/launcher3/proxy/StartActivityParams.java
new file mode 100644
index 0000000..bee8bb8
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/proxy/StartActivityParams.java
@@ -0,0 +1,109 @@
+/*
+ * 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.proxy;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+public class StartActivityParams implements Parcelable {
+
+    private static final String TAG = "StartActivityParams";
+
+    private final PendingIntent mPICallback;
+    public final int requestCode;
+
+    public Intent intent;
+
+    public IntentSender intentSender;
+    public Intent fillInIntent;
+    public int flagsMask;
+    public int flagsValues;
+    public int extraFlags;
+    public Bundle options;
+
+    public StartActivityParams(Activity activity, int requestCode) {
+        this(activity.createPendingResult(requestCode, new Intent(),
+                PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT), requestCode);
+    }
+
+    public StartActivityParams(PendingIntent pendingIntent, int requestCode) {
+        this.mPICallback = pendingIntent;
+        this.requestCode = requestCode;
+    }
+
+    private StartActivityParams(Parcel parcel) {
+        mPICallback = parcel.readTypedObject(PendingIntent.CREATOR);
+        requestCode = parcel.readInt();
+        intent = parcel.readTypedObject(Intent.CREATOR);
+
+        intentSender = parcel.readTypedObject(IntentSender.CREATOR);
+        fillInIntent = parcel.readTypedObject(Intent.CREATOR);
+        flagsMask = parcel.readInt();
+        flagsValues = parcel.readInt();
+        extraFlags = parcel.readInt();
+        options = parcel.readBundle();
+    }
+
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeTypedObject(mPICallback, flags);
+        parcel.writeInt(requestCode);
+        parcel.writeTypedObject(intent, flags);
+
+        parcel.writeTypedObject(intentSender, flags);
+        parcel.writeTypedObject(fillInIntent, flags);
+        parcel.writeInt(flagsMask);
+        parcel.writeInt(flagsValues);
+        parcel.writeInt(extraFlags);
+        parcel.writeBundle(options);
+    }
+
+    public void deliverResult(Context context, int resultCode, Intent data) {
+        try {
+            if (mPICallback != null) {
+                mPICallback.send(context, resultCode, data);
+            }
+        } catch (CanceledException e) {
+            Log.e(TAG, "Unable to send back result", e);
+        }
+    }
+
+    public static final Parcelable.Creator<StartActivityParams> CREATOR =
+            new Parcelable.Creator<StartActivityParams>() {
+                public StartActivityParams createFromParcel(Parcel source) {
+                    return new StartActivityParams(source);
+                }
+
+                public StartActivityParams[] newArray(int size) {
+                    return new StartActivityParams[size];
+                }
+            };
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
deleted file mode 100644
index 1eaa8bc..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
+++ /dev/null
@@ -1,89 +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;
-
-import static com.android.launcher3.LauncherAnimUtils.ALL_APPS_TRANSITION_MS;
-import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.allapps.AllAppsContainerView;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-
-/**
- * Definition for AllApps state
- */
-public class AllAppsState extends LauncherState {
-
-    private static final int STATE_FLAGS = FLAG_DISABLE_ACCESSIBILITY;
-
-    private static final PageAlphaProvider PAGE_ALPHA_PROVIDER = new PageAlphaProvider(DEACCEL_2) {
-        @Override
-        public float getPageAlpha(int pageIndex) {
-            return 0;
-        }
-    };
-
-    public AllAppsState(int id) {
-        super(id, ContainerType.ALLAPPS, ALL_APPS_TRANSITION_MS, STATE_FLAGS);
-    }
-
-    @Override
-    public void onStateEnabled(Launcher launcher) {
-        AbstractFloatingView.closeAllOpenViews(launcher);
-        dispatchWindowStateChanged(launcher);
-    }
-
-    @Override
-    public String getDescription(Launcher launcher) {
-        AllAppsContainerView appsView = launcher.getAppsView();
-        return appsView.getDescription();
-    }
-
-    @Override
-    public float getVerticalProgress(Launcher launcher) {
-        return 0f;
-    }
-
-    @Override
-    public float[] getWorkspaceScaleAndTranslation(Launcher launcher) {
-        float[] scaleAndTranslation = LauncherState.OVERVIEW.getWorkspaceScaleAndTranslation(
-                launcher);
-        scaleAndTranslation[0] = 1;
-        return scaleAndTranslation;
-    }
-
-    @Override
-    public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
-        return PAGE_ALPHA_PROVIDER;
-    }
-
-    @Override
-    public int getVisibleElements(Launcher launcher) {
-        return ALL_APPS_HEADER | ALL_APPS_HEADER_EXTRA | ALL_APPS_CONTENT;
-    }
-
-    @Override
-    public float[] getOverviewScaleAndTranslationYFactor(Launcher launcher) {
-        return new float[] {0.9f, -0.2f};
-    }
-
-    @Override
-    public LauncherState getHistoryForState(LauncherState previousState) {
-        return previousState == OVERVIEW ? OVERVIEW : NORMAL;
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BackButtonAlphaHandler.java b/quickstep/src/com/android/launcher3/uioverrides/BackButtonAlphaHandler.java
index 722f51b..693ae60 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BackButtonAlphaHandler.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BackButtonAlphaHandler.java
@@ -35,7 +35,7 @@
 
     public BackButtonAlphaHandler(Launcher launcher) {
         mLauncher = launcher;
-        mOverviewInteractionState = OverviewInteractionState.getInstance(mLauncher);
+        mOverviewInteractionState = OverviewInteractionState.INSTANCE.get(mLauncher);
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
new file mode 100644
index 0000000..f0204b9
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -0,0 +1,121 @@
+/*
+ * 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 com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_Y;
+import static com.android.launcher3.anim.AnimatorSetBuilder.FLAG_DONT_ANIMATE_OVERVIEW;
+import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+
+import android.util.FloatProperty;
+import android.view.View;
+import android.view.animation.Interpolator;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherState.ScaleAndTranslation;
+import com.android.launcher3.LauncherStateManager.AnimationConfig;
+import com.android.launcher3.LauncherStateManager.StateHandler;
+import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.anim.PropertySetter;
+
+import androidx.annotation.NonNull;
+
+/**
+ * State handler for recents view. Manages UI changes and animations for recents view based off the
+ * current {@link LauncherState}.
+ *
+ * @param <T> the recents view
+ */
+public abstract class BaseRecentsViewStateController<T extends View>
+        implements StateHandler {
+    protected final T mRecentsView;
+    protected final Launcher mLauncher;
+
+    public BaseRecentsViewStateController(@NonNull Launcher launcher) {
+        mLauncher = launcher;
+        mRecentsView = launcher.getOverviewPanel();
+    }
+
+    @Override
+    public void setState(@NonNull LauncherState state) {
+        ScaleAndTranslation scaleAndTranslation = state
+                .getOverviewScaleAndTranslation(mLauncher);
+        SCALE_PROPERTY.set(mRecentsView, scaleAndTranslation.scale);
+        float translationX = scaleAndTranslation.translationX;
+        if (mRecentsView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+            translationX = -translationX;
+        }
+        mRecentsView.setTranslationX(translationX);
+        mRecentsView.setTranslationY(scaleAndTranslation.translationY);
+        getContentAlphaProperty().set(mRecentsView, state.overviewUi ? 1f : 0);
+    }
+
+    @Override
+    public final void setStateWithAnimation(@NonNull final LauncherState toState,
+            @NonNull AnimatorSetBuilder builder, @NonNull AnimationConfig config) {
+        boolean playAtomicOverviewComponent = config.playAtomicOverviewScaleComponent()
+                || config.playAtomicOverviewPeekComponent();
+        if (!playAtomicOverviewComponent) {
+            // The entire recents animation is played atomically.
+            return;
+        }
+        if (builder.hasFlag(FLAG_DONT_ANIMATE_OVERVIEW)) {
+            return;
+        }
+        setStateWithAnimationInternal(toState, builder, config);
+    }
+
+    /**
+     * Core logic for animating the recents view UI.
+     *
+     * @param toState state to animate to
+     * @param builder animator set builder
+     * @param config current animation config
+     */
+    void setStateWithAnimationInternal(@NonNull final LauncherState toState,
+            @NonNull AnimatorSetBuilder builder, @NonNull AnimationConfig config) {
+        PropertySetter setter = config.getPropertySetter(builder);
+        ScaleAndTranslation scaleAndTranslation = toState.getOverviewScaleAndTranslation(mLauncher);
+        Interpolator scaleInterpolator = builder.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR);
+        setter.setFloat(mRecentsView, SCALE_PROPERTY, scaleAndTranslation.scale, scaleInterpolator);
+        Interpolator translateXInterpolator = builder.getInterpolator(
+                ANIM_OVERVIEW_TRANSLATE_X, LINEAR);
+        Interpolator translateYInterpolator = builder.getInterpolator(
+                ANIM_OVERVIEW_TRANSLATE_Y, LINEAR);
+        float translationX = scaleAndTranslation.translationX;
+        if (mRecentsView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+            translationX = -translationX;
+        }
+        setter.setFloat(mRecentsView, View.TRANSLATION_X, translationX, translateXInterpolator);
+        setter.setFloat(mRecentsView, View.TRANSLATION_Y, scaleAndTranslation.translationY,
+                translateYInterpolator);
+        setter.setFloat(mRecentsView, getContentAlphaProperty(), toState.overviewUi ? 1 : 0,
+                builder.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
+    }
+
+    /**
+     * Get property for content alpha for the recents view.
+     *
+     * @return the float property for the view's content alpha
+     */
+    abstract FloatProperty getContentAlphaProperty();
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java
deleted file mode 100644
index 2645302..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java
+++ /dev/null
@@ -1,85 +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 android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Rect;
-
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
-import com.android.quickstep.QuickScrubController;
-import com.android.quickstep.views.RecentsView;
-
-/**
- * Extension of overview state used for QuickScrub
- */
-public class FastOverviewState extends OverviewState {
-
-    private static final float MAX_PREVIEW_SCALE_UP = 1.3f;
-    /**
-     * Vertical transition of the task previews relative to the full container.
-     */
-    public static final float OVERVIEW_TRANSLATION_FACTOR = 0.4f;
-
-    private static final int STATE_FLAGS = FLAG_DISABLE_RESTORE | FLAG_DISABLE_INTERACTION
-            | FLAG_OVERVIEW_UI | FLAG_HIDE_BACK_BUTTON | FLAG_DISABLE_ACCESSIBILITY;
-
-    public FastOverviewState(int id) {
-        super(id, QuickScrubController.QUICK_SCRUB_FROM_HOME_START_DURATION, STATE_FLAGS);
-    }
-
-    @Override
-    public void onStateTransitionEnd(Launcher launcher) {
-        super.onStateTransitionEnd(launcher);
-        RecentsView recentsView = launcher.getOverviewPanel();
-        recentsView.getQuickScrubController().onFinishedTransitionToQuickScrub();
-    }
-
-    @Override
-    public int getVisibleElements(Launcher launcher) {
-        return NONE;
-    }
-
-    @Override
-    public float[] getOverviewScaleAndTranslationYFactor(Launcher launcher) {
-        RecentsView recentsView = launcher.getOverviewPanel();
-        recentsView.getTaskSize(sTempRect);
-
-        return new float[] {getOverviewScale(launcher.getDeviceProfile(), sTempRect, launcher),
-                OVERVIEW_TRANSLATION_FACTOR};
-    }
-
-    public static float getOverviewScale(DeviceProfile dp, Rect taskRect, Context context) {
-        if (dp.isVerticalBarLayout()) {
-            return 1f;
-        }
-
-        Resources res = context.getResources();
-        float usedHeight = taskRect.height() + res.getDimension(R.dimen.task_thumbnail_top_margin);
-        float usedWidth = taskRect.width() + 2 * (res.getDimension(R.dimen.recents_page_spacing)
-                + res.getDimension(R.dimen.quickscrub_adjacent_visible_width));
-        return Math.min(Math.min(dp.availableHeightPx / usedHeight,
-                dp.availableWidthPx / usedWidth), MAX_PREVIEW_SCALE_UP);
-    }
-
-    @Override
-    public void onStateDisabled(Launcher launcher) {
-        super.onStateDisabled(launcher);
-        launcher.<RecentsView>getOverviewPanel().getQuickScrubController().cancelActiveQuickscrub();
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java
deleted file mode 100644
index 6d10619..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package com.android.launcher3.uioverrides;
-
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.quickstep.TouchInteractionService.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.LauncherStateManager.AnimationComponents;
-import com.android.launcher3.touch.AbstractStateChangeTouchController;
-import com.android.launcher3.touch.SwipeDetector;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
-import com.android.quickstep.RecentsModel;
-
-/**
- * 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, SwipeDetector.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() {
-        return LauncherLogProto.ContainerType.NAVBAR;
-    }
-
-    @Override
-    protected float getShiftRange() {
-        return mLauncher.getDragLayer().getWidth();
-    }
-
-    @Override
-    protected float initCurrentAnimation(@AnimationComponents 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) {
-            RecentsModel.getInstance(mLauncher).onOverviewShown(true, TAG);
-        }
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
deleted file mode 100644
index 7f956f8..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
+++ /dev/null
@@ -1,133 +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;
-
-import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
-import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
-import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE;
-
-import android.view.View;
-
-import com.android.launcher3.AbstractFloatingView;
-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.allapps.DiscoveryBounce;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.quickstep.RecentsModel;
-import com.android.quickstep.views.RecentsView;
-
-/**
- * Definition for overview state
- */
-public class OverviewState extends LauncherState {
-
-    private static final int STATE_FLAGS = FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED
-            | FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_DISABLE_ACCESSIBILITY;
-
-    public OverviewState(int id) {
-        this(id, OVERVIEW_TRANSITION_MS, STATE_FLAGS);
-    }
-
-    protected OverviewState(int id, int transitionDuration, int stateFlags) {
-        super(id, ContainerType.TASKSWITCHER, transitionDuration, stateFlags);
-    }
-
-    @Override
-    public float[] 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 float[]{scale, 0, -getDefaultSwipeHeight(launcher) * parallaxFactor};
-    }
-
-    @Override
-    public float[] getOverviewScaleAndTranslationYFactor(Launcher launcher) {
-        return new float[] {1f, 0f};
-    }
-
-    @Override
-    public void onStateEnabled(Launcher launcher) {
-        RecentsView rv = launcher.getOverviewPanel();
-        rv.setOverviewStateEnabled(true);
-        AbstractFloatingView.closeAllOpenViews(launcher);
-    }
-
-    @Override
-    public void onStateDisabled(Launcher launcher) {
-        RecentsView rv = launcher.getOverviewPanel();
-        rv.setOverviewStateEnabled(false);
-        RecentsModel.getInstance(launcher).resetAssistCache();
-    }
-
-    @Override
-    public void onStateTransitionEnd(Launcher launcher) {
-        launcher.getRotationHelper().setCurrentStateRequest(REQUEST_ROTATE);
-        DiscoveryBounce.showForOverviewIfNeeded(launcher);
-    }
-
-    public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
-        return new PageAlphaProvider(DEACCEL_2) {
-            @Override
-            public float getPageAlpha(int pageIndex) {
-                return 0;
-            }
-        };
-    }
-
-    @Override
-    public int getVisibleElements(Launcher launcher) {
-        if (launcher.getDeviceProfile().isVerticalBarLayout()) {
-            return VERTICAL_SWIPE_INDICATOR;
-        } else {
-            return HOTSEAT_SEARCH_BOX | VERTICAL_SWIPE_INDICATOR |
-                    (launcher.getAppsView().getFloatingHeaderView().hasVisibleContent()
-                            ? ALL_APPS_HEADER_EXTRA : HOTSEAT_ICONS);
-        }
-    }
-
-    @Override
-    public float getWorkspaceScrimAlpha(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 1 - (getDefaultSwipeHeight(launcher)
-                / launcher.getAllAppsController().getShiftRange());
-    }
-
-    @Override
-    public String getDescription(Launcher launcher) {
-        return launcher.getString(R.string.accessibility_desc_recent_apps);
-    }
-
-    public static float getDefaultSwipeHeight(Launcher launcher) {
-        DeviceProfile dp = launcher.getDeviceProfile();
-        return dp.allAppsCellHeightPx - dp.allAppsIconTextSizePx;
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewToAllAppsTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewToAllAppsTouchController.java
deleted file mode 100644
index 0f9b57f..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewToAllAppsTouchController.java
+++ /dev/null
@@ -1,80 +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;
-
-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.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);
-    }
-
-    @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 true;
-        } 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() {
-        return LauncherLogProto.ContainerType.WORKSPACE;
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
deleted file mode 100644
index 0eead88..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
+++ /dev/null
@@ -1,271 +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 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.AnimatorSetBuilder.ANIM_ALL_APPS_FADE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS;
-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 android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.view.MotionEvent;
-import android.view.animation.Interpolator;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager.AnimationComponents;
-import com.android.launcher3.allapps.AllAppsTransitionController;
-import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.anim.AnimatorSetBuilder;
-import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.touch.AbstractStateChangeTouchController;
-import com.android.launcher3.touch.SwipeDetector;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.quickstep.RecentsModel;
-import com.android.quickstep.TouchInteractionService;
-import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskView;
-
-/**
- * Touch controller for handling various state transitions in portrait UI.
- */
-public class PortraitStatesTouchController extends AbstractStateChangeTouchController {
-
-    private static final String TAG = "PortraitStatesTouchCtrl";
-
-    /**
-     * The progress at which all apps content will be fully visible when swiping up from overview.
-     */
-    private static final float ALL_APPS_CONTENT_FADE_THRESHOLD = 0.08f;
-
-    /**
-     * The progress at which recents will begin fading out when swiping up from overview.
-     */
-    private static final float RECENTS_FADE_THRESHOLD = 0.88f;
-
-    private InterpolatorWrapper mAllAppsInterpolatorWrapper = new InterpolatorWrapper();
-
-    // If true, we will finish the current animation instantly on second touch.
-    private boolean mFinishFastOnSecondTouch;
-
-
-    public PortraitStatesTouchController(Launcher l) {
-        super(l, SwipeDetector.VERTICAL);
-    }
-
-    @Override
-    protected boolean canInterceptTouch(MotionEvent ev) {
-        if (mCurrentAnimation != null) {
-            if (mFinishFastOnSecondTouch) {
-                // TODO: Animate to finish instead.
-                mCurrentAnimation.getAnimationPlayer().end();
-            }
-
-            AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
-            if (ev.getY() >= allAppsController.getShiftRange() * allAppsController.getProgress()) {
-                // 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();
-            }
-            return false;
-        }
-        if (mLauncher.isInState(ALL_APPS)) {
-            // In all-apps only listen if the container cannot scroll itself
-            if (!mLauncher.getAppsView().shouldContainerScroll(ev)) {
-                return false;
-            }
-        } else {
-            // For all other states, only listen if the event originated below the hotseat height
-            DeviceProfile dp = mLauncher.getDeviceProfile();
-            int hotseatHeight = dp.hotseatBarSizePx + dp.getInsets().bottom;
-            if (ev.getY() < (mLauncher.getDragLayer().getHeight() - hotseatHeight)) {
-                return false;
-            }
-        }
-        if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
-            return false;
-        }
-        return true;
-    }
-
-    @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 (fromState == OVERVIEW) {
-            return isDragTowardPositive ? ALL_APPS : NORMAL;
-        } else if (fromState == NORMAL && isDragTowardPositive) {
-            return TouchInteractionService.isConnected() ? OVERVIEW : ALL_APPS;
-        }
-        return fromState;
-    }
-
-    @Override
-    protected int getLogContainerTypeForNormalState() {
-        return ContainerType.HOTSEAT;
-    }
-
-    private AnimatorSetBuilder getNormalToOverviewAnimation() {
-        mAllAppsInterpolatorWrapper.baseInterpolator = LINEAR;
-
-        AnimatorSetBuilder builder = new AnimatorSetBuilder();
-        builder.setInterpolator(ANIM_VERTICAL_PROGRESS, mAllAppsInterpolatorWrapper);
-        return builder;
-    }
-
-    public static AnimatorSetBuilder getOverviewToAllAppsAnimation() {
-        AnimatorSetBuilder builder = new AnimatorSetBuilder();
-        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 AnimatorSetBuilder getAllAppsToOverviewAnimation() {
-        AnimatorSetBuilder builder = new AnimatorSetBuilder();
-        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;
-    }
-
-    @Override
-    protected AnimatorSetBuilder getAnimatorSetBuilderForStates(LauncherState fromState,
-            LauncherState toState) {
-        AnimatorSetBuilder builder = new AnimatorSetBuilder();
-        if (fromState == NORMAL && toState == OVERVIEW) {
-            builder = getNormalToOverviewAnimation();
-        } else if (fromState == OVERVIEW && toState == ALL_APPS) {
-            builder = getOverviewToAllAppsAnimation();
-        } else if (fromState == ALL_APPS && toState == OVERVIEW) {
-            builder = getAllAppsToOverviewAnimation();
-        }
-        return builder;
-    }
-
-    @Override
-    protected float initCurrentAnimation(@AnimationComponents int animComponents) {
-        float range = getShiftRange();
-        long maxAccuracy = (long) (2 * range);
-
-        float startVerticalShift = mFromState.getVerticalProgress(mLauncher) * range;
-        float endVerticalShift = mToState.getVerticalProgress(mLauncher) * range;
-
-        float totalShift = endVerticalShift - startVerticalShift;
-
-        final AnimatorSetBuilder builder = totalShift == 0 ? new AnimatorSetBuilder()
-                : getAnimatorSetBuilderForStates(mFromState, mToState);
-
-        cancelPendingAnim();
-
-        RecentsView recentsView = mLauncher.getOverviewPanel();
-        TaskView taskView = recentsView.getTaskViewAt(recentsView.getNextPage());
-        if (recentsView.shouldSwipeDownLaunchApp() && mFromState == OVERVIEW && mToState == NORMAL
-                && taskView != null) {
-            // Reset the state manager, when changing the interaction mode
-            mLauncher.getStateManager().goToState(OVERVIEW, false /* animate */);
-            mPendingAnimation = recentsView.createTaskLauncherAnimation(taskView, maxAccuracy);
-            mPendingAnimation.anim.setInterpolator(Interpolators.ZOOM_IN);
-
-            Runnable onCancelRunnable = () -> {
-                cancelPendingAnim();
-                clearState();
-            };
-            mCurrentAnimation = AnimatorPlaybackController.wrap(mPendingAnimation.anim, maxAccuracy,
-                    onCancelRunnable);
-            mLauncher.getStateManager().setCurrentUserControlledAnimation(mCurrentAnimation);
-        } else {
-            mCurrentAnimation = mLauncher.getStateManager()
-                    .createAnimationToNewWorkspace(mToState, builder, maxAccuracy, this::clearState,
-                            animComponents);
-        }
-
-        if (totalShift == 0) {
-            totalShift = Math.signum(mFromState.ordinal - mToState.ordinal)
-                    * OverviewState.getDefaultSwipeHeight(mLauncher);
-        }
-        return 1 / totalShift;
-    }
-
-    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 (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);
-        if (mStartState == NORMAL && targetState == OVERVIEW) {
-            RecentsModel.getInstance(mLauncher).onOverviewShown(true, TAG);
-        }
-    }
-
-    private static class InterpolatorWrapper implements Interpolator {
-
-        public TimeInterpolator baseInterpolator = LINEAR;
-
-        @Override
-        public float getInterpolation(float v) {
-            return baseInterpolator.getInterpolation(v);
-        }
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
deleted file mode 100644
index abd2846..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ /dev/null
@@ -1,104 +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;
-
-import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
-import static com.android.launcher3.LauncherState.FAST_OVERVIEW;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
-import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_START_INTERPOLATOR;
-import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_TRANSLATION_Y_FACTOR;
-import static com.android.quickstep.views.LauncherRecentsView.TRANSLATION_Y_FACTOR;
-import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
-
-import android.animation.ValueAnimator;
-import android.annotation.TargetApi;
-import android.os.Build;
-import android.view.animation.Interpolator;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager.AnimationConfig;
-import com.android.launcher3.LauncherStateManager.StateHandler;
-import com.android.launcher3.anim.AnimatorSetBuilder;
-import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.anim.PropertySetter;
-import com.android.quickstep.views.LauncherRecentsView;
-
-@TargetApi(Build.VERSION_CODES.O)
-public class RecentsViewStateController implements StateHandler {
-
-    private final Launcher mLauncher;
-    private final LauncherRecentsView mRecentsView;
-
-    public RecentsViewStateController(Launcher launcher) {
-        mLauncher = launcher;
-        mRecentsView = launcher.getOverviewPanel();
-    }
-
-    @Override
-    public void setState(LauncherState state) {
-        mRecentsView.setContentAlpha(state.overviewUi ? 1 : 0);
-        float[] scaleTranslationYFactor = state.getOverviewScaleAndTranslationYFactor(mLauncher);
-        SCALE_PROPERTY.set(mRecentsView, scaleTranslationYFactor[0]);
-        mRecentsView.setTranslationYFactor(scaleTranslationYFactor[1]);
-        if (state.overviewUi) {
-            mRecentsView.updateEmptyMessage();
-            mRecentsView.resetTaskVisuals();
-        }
-    }
-
-    @Override
-    public void setStateWithAnimation(final LauncherState toState,
-            AnimatorSetBuilder builder, AnimationConfig config) {
-        if (!config.playAtomicComponent()) {
-            // The entire recents animation is played atomically.
-            return;
-        }
-        PropertySetter setter = config.getPropertySetter(builder);
-        float[] scaleTranslationYFactor = toState.getOverviewScaleAndTranslationYFactor(mLauncher);
-        Interpolator scaleAndTransYInterpolator = builder.getInterpolator(
-                ANIM_OVERVIEW_SCALE, LINEAR);
-        if (mLauncher.getStateManager().getState() == OVERVIEW && toState == FAST_OVERVIEW) {
-            scaleAndTransYInterpolator = Interpolators.clampToProgress(
-                    QUICK_SCRUB_START_INTERPOLATOR, 0, QUICK_SCRUB_TRANSLATION_Y_FACTOR);
-        }
-        setter.setFloat(mRecentsView, SCALE_PROPERTY, scaleTranslationYFactor[0],
-                scaleAndTransYInterpolator);
-        setter.setFloat(mRecentsView, TRANSLATION_Y_FACTOR, scaleTranslationYFactor[1],
-                scaleAndTransYInterpolator);
-        setter.setFloat(mRecentsView, CONTENT_ALPHA, toState.overviewUi ? 1 : 0,
-                builder.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
-
-        if (!toState.overviewUi) {
-            builder.addOnFinishRunnable(mRecentsView::resetTaskVisuals);
-        }
-
-        if (toState.overviewUi) {
-            ValueAnimator updateAnim = ValueAnimator.ofFloat(0, 1);
-            updateAnim.addUpdateListener(valueAnimator -> {
-                // While animating into recents, update the visible task data as needed
-                mRecentsView.loadVisibleTaskData();
-            });
-            updateAnim.setDuration(config.duration);
-            builder.play(updateAnim);
-            mRecentsView.updateEmptyMessage();
-        }
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
deleted file mode 100644
index cfd4119..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
+++ /dev/null
@@ -1,290 +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 com.android.launcher3.Utilities.SINGLE_FRAME_MS;
-import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.view.MotionEvent;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.LauncherAnimUtils;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.touch.SwipeDetector;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.launcher3.util.FlingBlockCheck;
-import com.android.launcher3.util.PendingAnimation;
-import com.android.launcher3.util.TouchController;
-import com.android.launcher3.views.BaseDragLayer;
-import com.android.quickstep.OverviewInteractionState;
-import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskView;
-
-/**
- * Touch controller for handling task view card swipes
- */
-public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
-        extends AnimatorListenerAdapter implements TouchController, SwipeDetector.Listener {
-
-    private static final String TAG = "OverviewSwipeController";
-
-    // Progress after which the transition is assumed to be a success in case user does not fling
-    private static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
-
-    protected final T mActivity;
-    private final SwipeDetector mDetector;
-    private final RecentsView mRecentsView;
-    private final int[] mTempCords = new int[2];
-
-    private PendingAnimation mPendingAnimation;
-    private AnimatorPlaybackController mCurrentAnimation;
-    private boolean mCurrentAnimationIsGoingUp;
-
-    private boolean mNoIntercept;
-
-    private float mDisplacementShift;
-    private float mProgressMultiplier;
-    private float mEndDisplacement;
-    private FlingBlockCheck mFlingBlockCheck = new FlingBlockCheck();
-
-    private TaskView mTaskBeingDragged;
-
-    public TaskViewTouchController(T activity) {
-        mActivity = activity;
-        mRecentsView = activity.getOverviewPanel();
-        mDetector = new SwipeDetector(activity, this, SwipeDetector.VERTICAL);
-    }
-
-    private boolean canInterceptTouch() {
-        if (mCurrentAnimation != null) {
-            // If we are already animating from a previous state, we can intercept.
-            return true;
-        }
-        if (AbstractFloatingView.getTopOpenView(mActivity) != null) {
-            return false;
-        }
-        return isRecentsInteractive();
-    }
-
-    protected abstract boolean isRecentsInteractive();
-
-    protected void onUserControlledAnimationCreated(AnimatorPlaybackController animController) {
-    }
-
-    @Override
-    public void onAnimationCancel(Animator animation) {
-        if (mCurrentAnimation != null && animation == mCurrentAnimation.getTarget()) {
-            clearState();
-        }
-    }
-
-    @Override
-    public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
-        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            mNoIntercept = !canInterceptTouch();
-            if (mNoIntercept) {
-                return false;
-            }
-
-            // Now figure out which direction scroll events the controller will start
-            // calling the callbacks.
-            int directionsToDetectScroll = 0;
-            boolean ignoreSlopWhenSettling = false;
-            if (mCurrentAnimation != null) {
-                directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
-                ignoreSlopWhenSettling = true;
-            } else {
-                mTaskBeingDragged = null;
-
-                for (int i = 0; i < mRecentsView.getTaskViewCount(); i++) {
-                    TaskView view = mRecentsView.getTaskViewAt(i);
-                    if (mRecentsView.isTaskViewVisible(view) && mActivity.getDragLayer()
-                            .isEventOverView(view, ev)) {
-                        mTaskBeingDragged = view;
-                        if (!OverviewInteractionState.getInstance(mActivity)
-                                .isSwipeUpGestureEnabled()) {
-                            // Don't allow swipe down to open if we don't support swipe up
-                            // to enter overview.
-                            directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE;
-                        } else {
-                            // The task can be dragged up to dismiss it,
-                            // and down to open if it's the current page.
-                            directionsToDetectScroll = i == mRecentsView.getCurrentPage()
-                                    ? SwipeDetector.DIRECTION_BOTH : SwipeDetector.DIRECTION_POSITIVE;
-                        }
-                        break;
-                    }
-                }
-                if (mTaskBeingDragged == null) {
-                    mNoIntercept = true;
-                    return false;
-                }
-            }
-
-            mDetector.setDetectableScrollConditions(
-                    directionsToDetectScroll, ignoreSlopWhenSettling);
-        }
-
-        if (mNoIntercept) {
-            return false;
-        }
-
-        onControllerTouchEvent(ev);
-        return mDetector.isDraggingOrSettling();
-    }
-
-    @Override
-    public boolean onControllerTouchEvent(MotionEvent ev) {
-        return mDetector.onTouchEvent(ev);
-    }
-
-    private void reInitAnimationController(boolean goingUp) {
-        if (mCurrentAnimation != null && mCurrentAnimationIsGoingUp == goingUp) {
-            // No need to init
-            return;
-        }
-        int scrollDirections = mDetector.getScrollDirections();
-        if (goingUp && ((scrollDirections & SwipeDetector.DIRECTION_POSITIVE) == 0)
-                || !goingUp && ((scrollDirections & SwipeDetector.DIRECTION_NEGATIVE) == 0)) {
-            // 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;
-        }
-
-        mCurrentAnimationIsGoingUp = goingUp;
-        BaseDragLayer dl = mActivity.getDragLayer();
-        long maxDuration = (long) (2 * dl.getHeight());
-
-        if (goingUp) {
-            mPendingAnimation = mRecentsView.createTaskDismissAnimation(mTaskBeingDragged,
-                    true /* animateTaskView */, true /* removeTask */, maxDuration);
-
-            mEndDisplacement = -mTaskBeingDragged.getHeight();
-        } else {
-            mPendingAnimation = mRecentsView.createTaskLauncherAnimation(
-                    mTaskBeingDragged, maxDuration);
-            mPendingAnimation.anim.setInterpolator(Interpolators.ZOOM_IN);
-
-            mTempCords[1] = mTaskBeingDragged.getHeight();
-            dl.getDescendantCoordRelativeToSelf(mTaskBeingDragged, mTempCords);
-            mEndDisplacement = dl.getHeight() - mTempCords[1];
-        }
-
-        if (mCurrentAnimation != null) {
-            mCurrentAnimation.setOnCancelRunnable(null);
-        }
-        mCurrentAnimation = AnimatorPlaybackController
-                .wrap(mPendingAnimation.anim, maxDuration, this::clearState);
-        onUserControlledAnimationCreated(mCurrentAnimation);
-        mCurrentAnimation.getTarget().addListener(this);
-        mCurrentAnimation.dispatchOnStart();
-        mProgressMultiplier = 1 / mEndDisplacement;
-    }
-
-    @Override
-    public void onDragStart(boolean start) {
-        if (mCurrentAnimation == null) {
-            reInitAnimationController(mDetector.wasInitialTouchPositive());
-            mDisplacementShift = 0;
-        } else {
-            mDisplacementShift = mCurrentAnimation.getProgressFraction() / mProgressMultiplier;
-            mCurrentAnimation.pause();
-        }
-        mFlingBlockCheck.unblockFling();
-    }
-
-    @Override
-    public boolean onDrag(float displacement, float velocity) {
-        float totalDisplacement = displacement + mDisplacementShift;
-        boolean isGoingUp =
-                totalDisplacement == 0 ? mCurrentAnimationIsGoingUp : totalDisplacement < 0;
-        if (isGoingUp != mCurrentAnimationIsGoingUp) {
-            reInitAnimationController(isGoingUp);
-            mFlingBlockCheck.blockFling();
-        } else {
-            mFlingBlockCheck.onEvent();
-        }
-        mCurrentAnimation.setPlayFraction(totalDisplacement * mProgressMultiplier);
-        return true;
-    }
-
-    @Override
-    public void onDragEnd(float velocity, boolean fling) {
-        final boolean goingToEnd;
-        final int logAction;
-        boolean blockedFling = fling && mFlingBlockCheck.isBlocked();
-        if (blockedFling) {
-            fling = false;
-        }
-        float progress = mCurrentAnimation.getProgressFraction();
-        float interpolatedProgress = mCurrentAnimation.getInterpolator().getInterpolation(progress);
-        if (fling) {
-            logAction = Touch.FLING;
-            boolean goingUp = velocity < 0;
-            goingToEnd = goingUp == mCurrentAnimationIsGoingUp;
-        } else {
-            logAction = Touch.SWIPE;
-            goingToEnd = interpolatedProgress > SUCCESS_TRANSITION_PROGRESS;
-        }
-        long animationDuration = SwipeDetector.calculateDuration(
-                velocity, goingToEnd ? (1 - progress) : progress);
-        if (blockedFling && !goingToEnd) {
-            animationDuration *= LauncherAnimUtils.blockedFlingDurationFactor(velocity);
-        }
-
-        float nextFrameProgress = Utilities.boundToRange(
-                progress + velocity * SINGLE_FRAME_MS / Math.abs(mEndDisplacement), 0f, 1f);
-
-        mCurrentAnimation.setEndAction(() -> onCurrentAnimationEnd(goingToEnd, logAction));
-
-        ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
-        anim.setFloatValues(nextFrameProgress, goingToEnd ? 1f : 0f);
-        anim.setDuration(animationDuration);
-        anim.setInterpolator(scrollInterpolatorForVelocity(velocity));
-        anim.start();
-    }
-
-    private void onCurrentAnimationEnd(boolean wasSuccess, int logAction) {
-        if (mPendingAnimation != null) {
-            mPendingAnimation.finish(wasSuccess, logAction);
-            mPendingAnimation = null;
-        }
-        clearState();
-    }
-
-    private void clearState() {
-        mDetector.finishedScrolling();
-        mDetector.setDetectableScrollConditions(0, false);
-        mTaskBeingDragged = null;
-        mCurrentAnimation = null;
-        if (mPendingAnimation != null) {
-            mPendingAnimation.finish(false, Touch.SWIPE);
-            mPendingAnimation = null;
-        }
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index 2d0946b..97cd38a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -16,76 +16,70 @@
 
 package com.android.launcher3.uioverrides;
 
-import static android.view.View.VISIBLE;
+import static android.app.Activity.RESULT_CANCELED;
+
 import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
 import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON;
-import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 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.allapps.DiscoveryBounce.BOUNCE_MAX_COUNT;
+import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_COUNT;
 import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN;
+import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_COUNT;
 import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_SEEN;
 
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
 import android.app.Activity;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.util.Base64;
 
 import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppTransitionManagerImpl;
 import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherState.ScaleAndTranslation;
 import com.android.launcher3.LauncherStateManager;
 import com.android.launcher3.LauncherStateManager.StateHandler;
+import com.android.launcher3.QuickstepAppTransitionManagerImpl;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.util.TouchController;
+import com.android.launcher3.proxy.ProxyActivityStarter;
+import com.android.launcher3.proxy.StartActivityParams;
 import com.android.quickstep.OverviewInteractionState;
 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.util.RemoteFadeOutAnimationListener;
-import com.android.quickstep.views.RecentsView;
 import com.android.systemui.shared.system.ActivityCompat;
-import com.android.systemui.shared.system.WindowManagerWrapper;
 
 import java.io.ByteArrayOutputStream;
 import java.io.PrintWriter;
 import java.util.zip.Deflater;
 
-public class UiFactory {
+public class UiFactory extends RecentsUiFactory {
 
-    public static TouchController[] createTouchControllers(Launcher launcher) {
-        boolean swipeUpEnabled = OverviewInteractionState.getInstance(launcher)
-                .isSwipeUpGestureEnabled();
-        if (!swipeUpEnabled) {
-            return new TouchController[] {
-                    launcher.getDragController(),
-                    new OverviewToAllAppsTouchController(launcher),
-                    new LauncherTaskViewController(launcher)};
-        }
-        if (launcher.getDeviceProfile().isVerticalBarLayout()) {
-            return new TouchController[] {
-                    launcher.getDragController(),
-                    new OverviewToAllAppsTouchController(launcher),
-                    new LandscapeEdgeSwipeController(launcher),
-                    new LauncherTaskViewController(launcher)};
-        } else {
-            return new TouchController[] {
-                    launcher.getDragController(),
-                    new PortraitStatesTouchController(launcher),
-                    new LauncherTaskViewController(launcher)};
-        }
-    }
-
-    public static void setOnTouchControllersChangedListener(Context context, Runnable listener) {
-        OverviewInteractionState.getInstance(context).setOnSwipeUpSettingChangedListener(listener);
+    public static Runnable enableLiveUIChanges(Launcher launcher) {
+        NavigationModeChangeListener listener = m -> {
+            launcher.getDragLayer().recreateControllers();
+            launcher.getRotationHelper().setRotationHadDifferentUI(m != Mode.NO_BUTTON);
+        };
+        SysUINavigationMode mode = SysUINavigationMode.INSTANCE.get(launcher);
+        SysUINavigationMode.Mode m = mode.addModeChangeListener(listener);
+        launcher.getRotationHelper().setRotationHadDifferentUI(m != Mode.NO_BUTTON);
+        return () -> mode.removeModeChangeListener(listener);
     }
 
     public static StateHandler[] getStateHandler(Launcher launcher) {
-        return new StateHandler[] {launcher.getAllAppsController(), launcher.getWorkspace(),
-                new RecentsViewStateController(launcher), new BackButtonAlphaHandler(launcher)};
+        return new StateHandler[] {
+                launcher.getAllAppsController(),
+                launcher.getWorkspace(),
+                createRecentsViewStateController(launcher),
+                new BackButtonAlphaHandler(launcher)};
     }
 
     /**
@@ -100,34 +94,29 @@
             shouldBackButtonBeHidden = AbstractFloatingView.getTopOpenViewWithType(launcher,
                     TYPE_ALL & ~TYPE_HIDE_BACK_BUTTON) == null;
         }
-        OverviewInteractionState.getInstance(launcher)
+        OverviewInteractionState.INSTANCE.get(launcher)
                 .setBackButtonAlpha(shouldBackButtonBeHidden ? 0 : 1, true /* animate */);
-    }
-
-    public static void resetOverview(Launcher launcher) {
-        RecentsView recents = launcher.getOverviewPanel();
-        recents.reset();
+        if (launcher != null && launcher.getDragLayer() != null) {
+            launcher.getRootView().setDisallowBackGesture(shouldBackButtonBeHidden);
+        }
     }
 
     public static void onCreate(Launcher launcher) {
         if (!launcher.getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)) {
             launcher.getStateManager().addStateListener(new LauncherStateManager.StateListener() {
                 @Override
-                public void onStateSetImmediately(LauncherState state) {
-                }
-
-                @Override
                 public void onStateTransitionStart(LauncherState toState) {
                 }
 
                 @Override
                 public void onStateTransitionComplete(LauncherState finalState) {
-                    boolean swipeUpEnabled = OverviewInteractionState.getInstance(launcher)
-                            .isSwipeUpGestureEnabled();
+                    boolean swipeUpEnabled = SysUINavigationMode.INSTANCE.get(launcher).getMode()
+                            .hasGestures;
                     LauncherState prevState = launcher.getStateManager().getLastState();
 
                     if (((swipeUpEnabled && finalState == OVERVIEW) || (!swipeUpEnabled
-                            && finalState == ALL_APPS && prevState == NORMAL))) {
+                            && finalState == ALL_APPS && prevState == NORMAL) || BOUNCE_MAX_COUNT <=
+                            launcher.getSharedPrefs().getInt(HOME_BOUNCE_COUNT, 0))) {
                         launcher.getSharedPrefs().edit().putBoolean(HOME_BOUNCE_SEEN, true).apply();
                         launcher.getStateManager().removeStateListener(this);
                     }
@@ -138,10 +127,6 @@
         if (!launcher.getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false)) {
             launcher.getStateManager().addStateListener(new LauncherStateManager.StateListener() {
                 @Override
-                public void onStateSetImmediately(LauncherState state) {
-                }
-
-                @Override
                 public void onStateTransitionStart(LauncherState toState) {
                 }
 
@@ -149,7 +134,8 @@
                 public void onStateTransitionComplete(LauncherState finalState) {
                     LauncherState prevState = launcher.getStateManager().getLastState();
 
-                    if (finalState == ALL_APPS && prevState == OVERVIEW) {
+                    if ((finalState == ALL_APPS && prevState == OVERVIEW) || BOUNCE_MAX_COUNT <=
+                            launcher.getSharedPrefs().getInt(SHELF_BOUNCE_COUNT, 0)) {
                         launcher.getSharedPrefs().edit().putBoolean(SHELF_BOUNCE_SEEN, true).apply();
                         launcher.getStateManager().removeStateListener(this);
                     }
@@ -158,36 +144,16 @@
         }
     }
 
-    public static void onStart(Context context) {
-        RecentsModel model = RecentsModel.getInstance(context);
-        if (model != null) {
-            model.onStart();
-        }
-    }
-
     public static void onEnterAnimationComplete(Context context) {
         // After the transition to home, enable the high-res thumbnail loader if it wasn't enabled
-        // as a part of quickstep/scrub, so that high-res thumbnails can load the next time we
-        // enter overview
-        RecentsModel.getInstance(context).getRecentsTaskLoader()
-                .getHighResThumbnailLoader().setVisible(true);
-    }
-
-    public static void onLauncherStateOrResumeChanged(Launcher launcher) {
-        LauncherState state = launcher.getStateManager().getState();
-        DeviceProfile profile = launcher.getDeviceProfile();
-        WindowManagerWrapper.getInstance().setShelfHeight(
-                (state == NORMAL || state == OVERVIEW) && launcher.isUserActive()
-                        && !profile.isVerticalBarLayout(),
-                profile.hotseatBarSizePx);
-
-        if (state == NORMAL) {
-            launcher.<RecentsView>getOverviewPanel().setSwipeDownShouldLaunchApp(false);
-        }
+        // as a part of quickstep, so that high-res thumbnails can load the next time we enter
+        // overview
+        RecentsModel.INSTANCE.get(context).getThumbnailCache()
+                .getHighResLoadingState().setVisible(true);
     }
 
     public static void onTrimMemory(Context context, int level) {
-        RecentsModel model = RecentsModel.getInstance(context);
+        RecentsModel model = RecentsModel.INSTANCE.get(context);
         if (model != null) {
             model.onTrimMemory(level);
         }
@@ -195,8 +161,8 @@
 
     public static void useFadeOutAnimationForLauncherStart(Launcher launcher,
             CancellationSignal cancellationSignal) {
-        LauncherAppTransitionManagerImpl appTransitionManager =
-                (LauncherAppTransitionManagerImpl) launcher.getAppTransitionManager();
+        QuickstepAppTransitionManagerImpl appTransitionManager =
+                (QuickstepAppTransitionManagerImpl) launcher.getAppTransitionManager();
         appTransitionManager.setRemoteAnimationProvider((targets) -> {
 
             // On the first call clear the reference.
@@ -236,27 +202,46 @@
         return true;
     }
 
-    public static void prepareToShowOverview(Launcher launcher) {
-        RecentsView overview = launcher.getOverviewPanel();
-        if (overview.getVisibility() != VISIBLE || overview.getContentAlpha() == 0) {
-            SCALE_PROPERTY.set(overview, 1.33f);
-        }
+    public static boolean startIntentSenderForResult(Activity activity, IntentSender intent,
+            int requestCode, Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags,
+            Bundle options) {
+        StartActivityParams params = new StartActivityParams(activity, requestCode);
+        params.intentSender = intent;
+        params.fillInIntent = fillInIntent;
+        params.flagsMask = flagsMask;
+        params.flagsValues = flagsValues;
+        params.extraFlags = extraFlags;
+        params.options = options;
+        ((Context) activity).startActivity(ProxyActivityStarter.getLaunchIntent(activity, params));
+        return true;
     }
 
-    private static class LauncherTaskViewController extends TaskViewTouchController<Launcher> {
+    public static boolean startActivityForResult(Activity activity, Intent intent, int requestCode,
+            Bundle options) {
+        StartActivityParams params = new StartActivityParams(activity, requestCode);
+        params.intent = intent;
+        params.options = options;
+        activity.startActivity(ProxyActivityStarter.getLaunchIntent(activity, params));
+        return true;
+    }
 
-        public LauncherTaskViewController(Launcher activity) {
-            super(activity);
-        }
+    /**
+     * Removes any active ProxyActivityStarter task and sends RESULT_CANCELED to Launcher.
+     *
+     * ProxyActivityStarter is started with clear task to reset the task after which it removes the
+     * task itself.
+     */
+    public static void resetPendingActivityResults(Launcher launcher, int requestCode) {
+        launcher.onActivityResult(requestCode, RESULT_CANCELED, null);
+        launcher.startActivity(ProxyActivityStarter.getLaunchIntent(launcher, null));
+    }
 
-        @Override
-        protected boolean isRecentsInteractive() {
-            return mActivity.isInState(OVERVIEW);
+    public static ScaleAndTranslation getOverviewScaleAndTranslationForNormalState(Launcher l) {
+        if (SysUINavigationMode.getMode(l) == Mode.NO_BUTTON) {
+            float offscreenTranslationX = l.getDeviceProfile().widthPx
+                    - l.getOverviewPanel().getPaddingStart();
+            return new ScaleAndTranslation(1f, offscreenTranslationX, 0f);
         }
-
-        @Override
-        protected void onUserControlledAnimationCreated(AnimatorPlaybackController animController) {
-            mActivity.getStateManager().setCurrentUserControlledAnimation(animController);
-        }
+        return new ScaleAndTranslation(1.1f, 0f, 0f);
     }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/WallpaperColorInfo.java b/quickstep/src/com/android/launcher3/uioverrides/WallpaperColorInfo.java
index 8218517..711e59a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/WallpaperColorInfo.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/WallpaperColorInfo.java
@@ -35,6 +35,10 @@
 @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;
+
     private static final Object sInstanceLock = new Object();
     private static WallpaperColorInfo sInstance;
 
@@ -79,6 +83,10 @@
         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) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginEnablerImpl.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginEnablerImpl.java
new file mode 100644
index 0000000..5afeca7
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginEnablerImpl.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.launcher3.uioverrides.plugins;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import com.android.launcher3.Utilities;
+import com.android.systemui.shared.plugins.PluginEnabler;
+
+import androidx.preference.PreferenceDataStore;
+
+public class PluginEnablerImpl extends PreferenceDataStore implements PluginEnabler {
+
+    private static final String PREFIX_PLUGIN_ENABLED = "PLUGIN_ENABLED_";
+
+    final private SharedPreferences mSharedPrefs;
+
+    public PluginEnablerImpl(Context context) {
+        mSharedPrefs = Utilities.getDevicePrefs(context);
+    }
+
+    @Override
+    public void setEnabled(ComponentName component) {
+        setState(component, true);
+    }
+
+    @Override
+    public void setDisabled(ComponentName component, int reason) {
+        setState(component, reason == ENABLED);
+    }
+
+    private void setState(ComponentName component, boolean enabled) {
+        putBoolean(pluginEnabledKey(component), enabled);
+    }
+
+    @Override
+    public boolean isEnabled(ComponentName component) {
+        return getBoolean(pluginEnabledKey(component), true);
+    }
+
+    @Override
+    public int getDisableReason(ComponentName componentName) {
+        return isEnabled(componentName) ? ENABLED : DISABLED_MANUALLY;
+    }
+
+    @Override
+    public void putBoolean(String key, boolean value) {
+        mSharedPrefs.edit().putBoolean(key, value).apply();
+    }
+
+    @Override
+    public boolean getBoolean(String key, boolean defValue) {
+        return mSharedPrefs.getBoolean(key, defValue);
+    }
+
+    static String pluginEnabledKey(ComponentName cn) {
+        return PREFIX_PLUGIN_ENABLED + cn.flattenToString();
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java
new file mode 100644
index 0000000..910fa0d
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.launcher3.uioverrides.plugins;
+
+import android.content.Context;
+import android.os.Looper;
+
+import com.android.launcher3.LauncherModel;
+import com.android.systemui.shared.plugins.PluginInitializer;
+
+public class PluginInitializerImpl implements PluginInitializer {
+    @Override
+    public Looper getBgLooper() {
+        return LauncherModel.getWorkerLooper();
+    }
+
+    @Override
+    public void onPluginManagerInit() {
+    }
+
+    @Override
+    public String[] getWhitelistedPlugins(Context context) {
+        return new String[0];
+    }
+
+    @Override
+    public PluginEnablerImpl getPluginEnabler(Context context) {
+        return new PluginEnablerImpl(context);
+    }
+
+    @Override
+    public void handleWtfs() {
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
new file mode 100644
index 0000000..6e7c087
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.launcher3.uioverrides.plugins;
+
+import android.content.ComponentName;
+import android.content.Context;
+
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.systemui.plugins.Plugin;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManagerImpl;
+import com.android.systemui.shared.plugins.PluginPrefs;
+
+import java.util.Set;
+
+public class PluginManagerWrapper {
+
+    public static final MainThreadInitializedObject<PluginManagerWrapper> INSTANCE =
+            new MainThreadInitializedObject<>(PluginManagerWrapper::new);
+
+    public static final String PLUGIN_CHANGED = PluginManager.PLUGIN_CHANGED;
+
+    private final Context mContext;
+    private final PluginManager mPluginManager;
+    private final PluginEnablerImpl mPluginEnabler;
+
+    private PluginManagerWrapper(Context c) {
+        mContext = c;
+        PluginInitializerImpl pluginInitializer  = new PluginInitializerImpl();
+        mPluginManager = new PluginManagerImpl(c, pluginInitializer);
+        mPluginEnabler = pluginInitializer.getPluginEnabler(c);
+    }
+
+    public PluginEnablerImpl getPluginEnabler() {
+        return mPluginEnabler;
+    }
+
+    public void addPluginListener(PluginListener<? extends Plugin> listener, Class<?> pluginClass) {
+        addPluginListener(listener, pluginClass, false);
+    }
+
+    public void addPluginListener(PluginListener<? extends Plugin> listener, Class<?> pluginClass,
+            boolean allowMultiple) {
+        mPluginManager.addPluginListener(listener, pluginClass, allowMultiple);
+    }
+
+    public void removePluginListener(PluginListener<? extends Plugin> listener) {
+        mPluginManager.removePluginListener(listener);
+    }
+
+    public Set<String> getPluginActions() {
+        return new PluginPrefs(mContext).getPluginList();
+    }
+
+    /**
+     * Returns the string key used to store plugin enabled/disabled setting
+     */
+    public static String pluginEnabledKey(ComponentName cn) {
+        return PluginEnablerImpl.pluginEnabledKey(cn);
+    }
+
+    public static boolean hasPlugins(Context context) {
+        return PluginPrefs.hasPlugins(context);
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
new file mode 100644
index 0000000..85a9545
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -0,0 +1,97 @@
+/*
+ * 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.LauncherAnimUtils.ALL_APPS_TRANSITION_MS;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.quickstep.SysUINavigationMode;
+
+/**
+ * Definition for AllApps state
+ */
+public class AllAppsState extends LauncherState {
+
+    private static final int STATE_FLAGS = FLAG_DISABLE_ACCESSIBILITY;
+
+    private static final PageAlphaProvider PAGE_ALPHA_PROVIDER = new PageAlphaProvider(DEACCEL_2) {
+        @Override
+        public float getPageAlpha(int pageIndex) {
+            return 0;
+        }
+    };
+
+    public AllAppsState(int id) {
+        super(id, ContainerType.ALLAPPS, ALL_APPS_TRANSITION_MS, STATE_FLAGS);
+    }
+
+    @Override
+    public void onStateEnabled(Launcher launcher) {
+        AbstractFloatingView.closeAllOpenViews(launcher);
+        dispatchWindowStateChanged(launcher);
+    }
+
+    @Override
+    public String getDescription(Launcher launcher) {
+        AllAppsContainerView appsView = launcher.getAppsView();
+        return appsView.getDescription();
+    }
+
+    @Override
+    public float getVerticalProgress(Launcher launcher) {
+        return 0f;
+    }
+
+    @Override
+    public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
+        ScaleAndTranslation scaleAndTranslation = LauncherState.OVERVIEW
+                .getWorkspaceScaleAndTranslation(launcher);
+        if (SysUINavigationMode.getMode(launcher) == SysUINavigationMode.Mode.NO_BUTTON) {
+            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;
+        }
+        return scaleAndTranslation;
+    }
+
+    @Override
+    public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
+        return PAGE_ALPHA_PROVIDER;
+    }
+
+    @Override
+    public int getVisibleElements(Launcher launcher) {
+        return ALL_APPS_HEADER | ALL_APPS_HEADER_EXTRA | ALL_APPS_CONTENT;
+    }
+
+    @Override
+    public ScaleAndTranslation getOverviewScaleAndTranslation(Launcher launcher) {
+        float slightParallax = -launcher.getDeviceProfile().allAppsCellHeightPx * 0.3f;
+        return new ScaleAndTranslation(0.9f, 0f, slightParallax);
+    }
+
+    @Override
+    public LauncherState getHistoryForState(LauncherState previousState) {
+        return previousState == OVERVIEW ? OVERVIEW : NORMAL;
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java
new file mode 100644
index 0000000..0605953
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java
@@ -0,0 +1,79 @@
+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.LauncherStateManager.AnimationComponents;
+import com.android.launcher3.touch.AbstractStateChangeTouchController;
+import com.android.launcher3.touch.SwipeDetector;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
+import com.android.quickstep.RecentsModel;
+
+/**
+ * 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, SwipeDetector.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() {
+        return LauncherLogProto.ContainerType.NAVBAR;
+    }
+
+    @Override
+    protected float getShiftRange() {
+        return mLauncher.getDragLayer().getWidth();
+    }
+
+    @Override
+    protected float initCurrentAnimation(@AnimationComponents 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) {
+            RecentsModel.INSTANCE.get(mLauncher).onOverviewShown(true, TAG);
+        }
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
new file mode 100644
index 0000000..6030cea
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -0,0 +1,313 @@
+/*
+ * 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.touchcontrollers;
+
+import static com.android.launcher3.AbstractFloatingView.TYPE_ACCESSIBLE;
+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.AnimatorSetBuilder.ANIM_ALL_APPS_FADE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS;
+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.QUICKSTEP_SPRINGS;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
+
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.view.MotionEvent;
+import android.view.animation.Interpolator;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager.AnimationComponents;
+import com.android.launcher3.allapps.AllAppsTransitionController;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.touch.AbstractStateChangeTouchController;
+import com.android.launcher3.touch.SwipeDetector;
+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.OverviewInteractionState;
+import com.android.quickstep.RecentsModel;
+import com.android.quickstep.TouchInteractionService;
+import com.android.quickstep.util.LayoutUtils;
+import com.android.systemui.shared.system.QuickStepContract;
+
+/**
+ * Touch controller for handling various state transitions in portrait UI.
+ */
+public class PortraitStatesTouchController extends AbstractStateChangeTouchController {
+
+    private static final String TAG = "PortraitStatesTouchCtrl";
+
+    /**
+     * The progress at which all apps content will be fully visible when swiping up from overview.
+     */
+    private static final float ALL_APPS_CONTENT_FADE_THRESHOLD = 0.08f;
+
+    /**
+     * The progress at which recents will begin fading out when swiping up from overview.
+     */
+    private static final float RECENTS_FADE_THRESHOLD = 0.88f;
+
+    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) {
+        super(l, SwipeDetector.VERTICAL);
+        mOverviewPortraitStateTouchHelper = new PortraitOverviewStateTouchHelper(l);
+        mAllowDragToOverview = allowDragToOverview;
+    }
+
+    @Override
+    protected boolean canInterceptTouch(MotionEvent ev) {
+        if (mCurrentAnimation != null) {
+            if (mFinishFastOnSecondTouch) {
+                // TODO: Animate to finish instead.
+                mCurrentAnimation.skipToEnd();
+            }
+
+            AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
+            if (ev.getY() >= allAppsController.getShiftRange() * allAppsController.getProgress()) {
+                // 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();
+            }
+            return false;
+        }
+        if (mLauncher.isInState(ALL_APPS)) {
+            // In all-apps only listen if the container cannot scroll itself
+            if (!mLauncher.getAppsView().shouldContainerScroll(ev)) {
+                return false;
+            }
+        } else if (mLauncher.isInState(OVERVIEW)) {
+            if (!mOverviewPortraitStateTouchHelper.canInterceptTouch(ev)) {
+                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
+            if (!interceptAnywhere && !isTouchOverHotseat(mLauncher, ev)) {
+                return false;
+            }
+        }
+        if (AbstractFloatingView.getTopOpenViewWithType(mLauncher, TYPE_ACCESSIBLE) != null) {
+            return false;
+        }
+        return true;
+    }
+
+    @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 (fromState == OVERVIEW) {
+            return isDragTowardPositive ? ALL_APPS : NORMAL;
+        } else if (fromState == NORMAL && isDragTowardPositive) {
+            int stateFlags = OverviewInteractionState.INSTANCE.get(mLauncher)
+                    .getSystemUiStateFlags();
+            return mAllowDragToOverview && TouchInteractionService.isConnected()
+                    && (stateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0
+                    ? OVERVIEW : ALL_APPS;
+        }
+        return fromState;
+    }
+
+    @Override
+    protected int getLogContainerTypeForNormalState() {
+        return ContainerType.HOTSEAT;
+    }
+
+    private AnimatorSetBuilder getNormalToOverviewAnimation() {
+        mAllAppsInterpolatorWrapper.baseInterpolator = LINEAR;
+
+        AnimatorSetBuilder builder = new AnimatorSetBuilder();
+        builder.setInterpolator(ANIM_VERTICAL_PROGRESS, mAllAppsInterpolatorWrapper);
+        return builder;
+    }
+
+    public static AnimatorSetBuilder getOverviewToAllAppsAnimation() {
+        AnimatorSetBuilder builder = new AnimatorSetBuilder();
+        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 AnimatorSetBuilder getAllAppsToOverviewAnimation() {
+        AnimatorSetBuilder builder = new AnimatorSetBuilder();
+        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;
+    }
+
+    @Override
+    protected AnimatorSetBuilder getAnimatorSetBuilderForStates(LauncherState fromState,
+            LauncherState toState) {
+        AnimatorSetBuilder builder = new AnimatorSetBuilder();
+        if (fromState == NORMAL && toState == OVERVIEW) {
+            builder = getNormalToOverviewAnimation();
+        } else if (fromState == OVERVIEW && toState == ALL_APPS) {
+            builder = getOverviewToAllAppsAnimation();
+        } else if (fromState == ALL_APPS && toState == OVERVIEW) {
+            builder = getAllAppsToOverviewAnimation();
+        }
+        return builder;
+    }
+
+    @Override
+    protected float initCurrentAnimation(@AnimationComponents int animComponents) {
+        float range = getShiftRange();
+        long maxAccuracy = (long) (2 * range);
+
+        float startVerticalShift = mFromState.getVerticalProgress(mLauncher) * range;
+        float endVerticalShift = mToState.getVerticalProgress(mLauncher) * range;
+
+        float totalShift = endVerticalShift - startVerticalShift;
+
+        final AnimatorSetBuilder builder = totalShift == 0 ? new AnimatorSetBuilder()
+                : getAnimatorSetBuilderForStates(mFromState, mToState);
+        updateAnimatorBuilderOnReinit(builder);
+
+        cancelPendingAnim();
+
+        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);
+            mPendingAnimation.anim.setInterpolator(Interpolators.LINEAR);
+
+            Runnable onCancelRunnable = () -> {
+                cancelPendingAnim();
+                clearState();
+            };
+            mCurrentAnimation = AnimatorPlaybackController.wrap(mPendingAnimation.anim, maxAccuracy,
+                    onCancelRunnable);
+            mLauncher.getStateManager().setCurrentUserControlledAnimation(mCurrentAnimation);
+            totalShift = LayoutUtils.getShelfTrackingDistance(mLauncher,
+                    mLauncher.getDeviceProfile());
+        } else {
+            mCurrentAnimation = mLauncher.getStateManager()
+                    .createAnimationToNewWorkspace(mToState, builder, maxAccuracy, this::clearState,
+                            animComponents);
+        }
+
+        if (totalShift == 0) {
+            totalShift = Math.signum(mFromState.ordinal - mToState.ordinal)
+                    * OverviewState.getDefaultSwipeHeight(mLauncher);
+        }
+        return 1 / totalShift;
+    }
+
+    /**
+     * Give subclasses the chance to update the animation when we re-initialize towards a new state.
+     */
+    protected void updateAnimatorBuilderOnReinit(AnimatorSetBuilder builder) {
+    }
+
+    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 (QUICKSTEP_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);
+        if (mStartState == NORMAL && targetState == OVERVIEW) {
+            RecentsModel.INSTANCE.get(mLauncher).onOverviewShown(true, TAG);
+        }
+    }
+
+    /**
+     * Whether the motion event is over the hotseat.
+     *
+     * @param launcher the launcher activity
+     * @param ev the event to check
+     * @return true if the event is over the hotseat
+     */
+    static boolean isTouchOverHotseat(Launcher launcher, MotionEvent ev) {
+        DeviceProfile dp = launcher.getDeviceProfile();
+        int hotseatHeight = dp.hotseatBarSizePx + dp.getInsets().bottom;
+        return (ev.getY() >= (launcher.getDragLayer().getHeight() - hotseatHeight));
+    }
+
+    private static class InterpolatorWrapper implements Interpolator {
+
+        public TimeInterpolator baseInterpolator = LINEAR;
+
+        @Override
+        public float getInterpolation(float v) {
+            return baseInterpolator.getInterpolation(v);
+        }
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
new file mode 100644
index 0000000..fee1820
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
@@ -0,0 +1,135 @@
+/*
+ * 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.touchcontrollers;
+
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_MOVE;
+
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.touch.TouchEventTranslator;
+import com.android.launcher3.util.TouchController;
+import com.android.quickstep.RecentsModel;
+import com.android.systemui.shared.recents.ISystemUiProxy;
+
+import java.io.PrintWriter;
+
+/**
+ * TouchController for handling touch events that get sent to the StatusBar. Once the
+ * Once the event delta y passes the touch slop, the events start getting forwarded.
+ * All events are offset by initial Y value of the pointer.
+ */
+public class StatusBarTouchController implements TouchController {
+
+    private static final String TAG = "StatusBarController";
+
+    protected final Launcher mLauncher;
+    protected final TouchEventTranslator mTranslator;
+    private final float mTouchSlop;
+    private ISystemUiProxy mSysUiProxy;
+    private int mLastAction;
+
+    /* If {@code false}, this controller should not handle the input {@link MotionEvent}.*/
+    private boolean mCanIntercept;
+
+    public StatusBarTouchController(Launcher l) {
+        mLauncher = l;
+        // Guard against TAPs by increasing the touch slop.
+        mTouchSlop = 2 * ViewConfiguration.get(l).getScaledTouchSlop();
+        mTranslator = new TouchEventTranslator((MotionEvent ev)-> dispatchTouchEvent(ev));
+    }
+
+    @Override
+    public void dump(String prefix, PrintWriter writer) {
+        writer.println(prefix + "mCanIntercept:" + mCanIntercept);
+        writer.println(prefix + "mLastAction:" + MotionEvent.actionToString(mLastAction));
+        writer.println(prefix + "mSysUiProxy available:" + (mSysUiProxy != null));
+
+    }
+
+    private void dispatchTouchEvent(MotionEvent ev) {
+        try {
+            if (mSysUiProxy != null) {
+                mLastAction = ev.getActionMasked();
+                mSysUiProxy.onStatusBarMotionEvent(ev);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Remote exception on sysUiProxy.", e);
+        }
+    }
+
+    @Override
+    public final boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+        int action = ev.getActionMasked();
+        if (action == ACTION_DOWN) {
+            mCanIntercept = canInterceptTouch(ev);
+            if (!mCanIntercept) {
+                return false;
+            }
+            mTranslator.reset();
+            mTranslator.setDownParameters(0, ev);
+        } else if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
+            // Check!! should only set it only when threshold is not entered.
+            mTranslator.setDownParameters(ev.getActionIndex(), ev);
+        }
+        if (!mCanIntercept) {
+            return false;
+        }
+        if (action == ACTION_MOVE) {
+            float dy = ev.getY() - mTranslator.getDownY();
+            float dx = ev.getX() - mTranslator.getDownX();
+            if (dy > mTouchSlop && dy > Math.abs(dx)) {
+                mTranslator.dispatchDownEvents(ev);
+                mTranslator.processMotionEvent(ev);
+                return true;
+            }
+            if (Math.abs(dx) > mTouchSlop) {
+                mCanIntercept = false;
+            }
+        }
+        return false;
+    }
+
+
+    @Override
+    public final boolean onControllerTouchEvent(MotionEvent ev) {
+        mTranslator.processMotionEvent(ev);
+        return true;
+    }
+
+    private boolean canInterceptTouch(MotionEvent ev) {
+        if (!mLauncher.isInState(LauncherState.NORMAL) ||
+                AbstractFloatingView.getTopOpenViewWithType(mLauncher,
+                        AbstractFloatingView.TYPE_STATUS_BAR_SWIPE_DOWN_DISALLOW) != null) {
+            return false;
+        } else {
+            // For NORMAL state, only listen if the event originated above the navbar height
+            DeviceProfile dp = mLauncher.getDeviceProfile();
+            if (ev.getY() > (mLauncher.getDragLayer().getHeight() - dp.getInsets().bottom)) {
+                return false;
+            }
+        }
+        mSysUiProxy = RecentsModel.INSTANCE.get(mLauncher).getSystemUiProxy();
+        return mSysUiProxy != null;
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
index ced8754..cd2c9cb 100644
--- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java
+++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
@@ -15,63 +15,29 @@
  */
 package com.android.quickstep;
 
-import static android.view.View.TRANSLATION_Y;
-
-import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
-import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
-import static com.android.launcher3.LauncherState.FAST_OVERVIEW;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
-import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
-import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
-import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_BACK;
-import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_ROTATION;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
 import android.annotation.TargetApi;
-import android.app.ActivityManager.RunningTaskInfo;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Region;
 import android.os.Build;
 import android.os.Handler;
-import android.os.Looper;
-import android.support.annotation.Nullable;
-import android.support.annotation.UiThread;
+import android.view.MotionEvent;
 import android.view.View;
+import android.view.animation.Interpolator;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
 
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherInitListener;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.R;
-import com.android.launcher3.allapps.AllAppsTransitionController;
-import com.android.launcher3.allapps.DiscoveryBounce;
-import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.dragndrop.DragLayer;
-import com.android.launcher3.uioverrides.FastOverviewState;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
-import com.android.quickstep.TouchConsumer.InteractionType;
-import com.android.quickstep.util.ClipAnimationHelper;
-import com.android.quickstep.util.LayoutUtils;
 import com.android.quickstep.util.RemoteAnimationProvider;
 import com.android.quickstep.util.RemoteAnimationTargetSet;
-import com.android.quickstep.util.TransformedRect;
-import com.android.quickstep.views.LauncherLayoutListener;
-import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
-import java.util.Objects;
 import java.util.function.BiPredicate;
 import java.util.function.Consumer;
 
@@ -81,518 +47,55 @@
 @TargetApi(Build.VERSION_CODES.P)
 public interface ActivityControlHelper<T extends BaseDraggingActivity> {
 
-    LayoutListener createLayoutListener(T activity);
-
-    /**
-     * Updates the UI to indicate quick interaction.
-     */
-    void onQuickInteractionStart(T activity, @Nullable RunningTaskInfo taskInfo,
-            boolean activityVisible);
-
-    float getTranslationYForQuickScrub(TransformedRect targetRect, DeviceProfile dp,
-            Context context);
-
-    void executeOnWindowAvailable(T activity, Runnable action);
-
     void onTransitionCancelled(T activity, boolean activityVisible);
 
-    int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context,
-            @InteractionType int interactionType, TransformedRect outRect);
+    int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect);
 
-    void onSwipeUpComplete(T activity);
+    void onSwipeUpToRecentsComplete(T activity);
+
+    default void onSwipeUpToHomeComplete(T activity) { }
+    void onAssistantVisibilityChanged(float visibility);
+
+    @NonNull HomeAnimationFactory prepareHomeUI(T activity);
 
     AnimationFactory prepareRecentsUI(T activity, boolean activityVisible,
-            Consumer<AnimatorPlaybackController> callback);
+            boolean animateActivity, Consumer<AnimatorPlaybackController> callback);
 
     ActivityInitListener createActivityInitListener(BiPredicate<T, Boolean> onInitListener);
 
     @Nullable
     T getCreatedActivity();
 
-    @UiThread
-    @Nullable
-    RecentsView getVisibleRecentsView();
+    default boolean isResumed() {
+        BaseDraggingActivity activity = getCreatedActivity();
+        return activity != null && activity.hasBeenResumed();
+    }
 
     @UiThread
-    boolean switchToRecentsIfVisible(boolean fromRecentsButton);
+    @Nullable
+    <T extends View> T getVisibleRecentsView();
+
+    @UiThread
+    boolean switchToRecentsIfVisible(Runnable onCompleteCallback);
 
     Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target);
 
     boolean shouldMinimizeSplitScreen();
 
-    /**
-     * @return {@code true} if recents activity should be started immediately on touchDown,
-     *         {@code false} if it should deferred until some threshold is crossed.
-     */
-    boolean deferStartingActivity(int downHitTarget);
-
-    boolean supportsLongSwipe(T activity);
-
-    AlphaProperty getAlphaProperty(T activity);
-
-    /**
-     * Must return a non-null controller is supportsLongSwipe was true.
-     */
-    LongSwipeHelper getLongSwipeController(T activity, RemoteAnimationTargetSet targetSet);
+    default boolean deferStartingActivity(Region activeNavBarRegion, MotionEvent ev) {
+        return true;
+    }
 
     /**
      * Used for containerType in {@link com.android.launcher3.logging.UserEventDispatcher}
      */
     int getContainerType();
 
-    class LauncherActivityControllerHelper implements ActivityControlHelper<Launcher> {
+    boolean isInLiveTileMode();
 
-        @Override
-        public LayoutListener createLayoutListener(Launcher activity) {
-            return new LauncherLayoutListener(activity);
-        }
+    void onLaunchTaskFailed(T activity);
 
-        @Override
-        public void onQuickInteractionStart(Launcher activity, RunningTaskInfo taskInfo,
-                boolean activityVisible) {
-            LauncherState fromState = activity.getStateManager().getState();
-            activity.getStateManager().goToState(FAST_OVERVIEW, activityVisible);
-
-            QuickScrubController controller = activity.<RecentsView>getOverviewPanel()
-                    .getQuickScrubController();
-            controller.onQuickScrubStart(activityVisible && !fromState.overviewUi, this);
-        }
-
-        @Override
-        public float getTranslationYForQuickScrub(TransformedRect targetRect, DeviceProfile dp,
-                Context context) {
-            // The padding calculations are exactly same as that of RecentsView.setInsets
-            int topMargin = context.getResources()
-                    .getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
-            int paddingTop = targetRect.rect.top - topMargin - dp.getInsets().top;
-            int paddingBottom = dp.availableHeightPx + dp.getInsets().top - targetRect.rect.bottom;
-
-            return FastOverviewState.OVERVIEW_TRANSLATION_FACTOR * (paddingBottom - paddingTop);
-        }
-
-        @Override
-        public void executeOnWindowAvailable(Launcher activity, Runnable action) {
-            activity.getWorkspace().runOnOverlayHidden(action);
-        }
-
-        @Override
-        public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context,
-                @InteractionType int interactionType, TransformedRect outRect) {
-            LayoutUtils.calculateLauncherTaskSize(context, dp, outRect.rect);
-            if (interactionType == INTERACTION_QUICK_SCRUB) {
-                outRect.scale = FastOverviewState.getOverviewScale(dp, outRect.rect, context);
-            }
-            if (dp.isVerticalBarLayout()) {
-                Rect targetInsets = dp.getInsets();
-                int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right;
-                return dp.hotseatBarSizePx + hotseatInset;
-            } else {
-                int shelfHeight = dp.hotseatBarSizePx + dp.getInsets().bottom;
-                // Track slightly below the top of the shelf (between top and content).
-                return shelfHeight - dp.edgeMarginPx * 2;
-            }
-        }
-
-        @Override
-        public void onTransitionCancelled(Launcher activity, boolean activityVisible) {
-            LauncherState startState = activity.getStateManager().getRestState();
-            activity.getStateManager().goToState(startState, activityVisible);
-        }
-
-        @Override
-        public void onSwipeUpComplete(Launcher activity) {
-            // Re apply state in case we did something funky during the transition.
-            activity.getStateManager().reapplyState();
-            DiscoveryBounce.showForOverviewIfNeeded(activity);
-        }
-
-        @Override
-        public AnimationFactory prepareRecentsUI(Launcher activity, boolean activityVisible,
-                Consumer<AnimatorPlaybackController> callback) {
-            final LauncherState startState = activity.getStateManager().getState();
-
-            LauncherState resetState = startState;
-            if (startState.disableRestore) {
-                resetState = activity.getStateManager().getRestState();
-            }
-            activity.getStateManager().setRestState(resetState);
-
-            if (!activityVisible) {
-                // Since the launcher is not visible, we can safely reset the scroll position.
-                // This ensures then the next swipe up to all-apps starts from scroll 0.
-                activity.getAppsView().reset(false /* animate */);
-                activity.getStateManager().goToState(OVERVIEW, false);
-
-                // Optimization, hide the all apps view to prevent layout while initializing
-                activity.getAppsView().getContentView().setVisibility(View.GONE);
-            }
-
-            return new AnimationFactory() {
-                @Override
-                public void createActivityController(long transitionLength,
-                        @InteractionType int interactionType) {
-                    createActivityControllerInternal(activity, activityVisible, startState,
-                            transitionLength, interactionType, callback);
-                }
-
-                @Override
-                public void onTransitionCancelled() {
-                    activity.getStateManager().goToState(startState, false /* animate */);
-                }
-            };
-        }
-
-        private void createActivityControllerInternal(Launcher activity, boolean wasVisible,
-                LauncherState startState, long transitionLength,
-                @InteractionType int interactionType,
-                Consumer<AnimatorPlaybackController> callback) {
-            LauncherState endState = interactionType == INTERACTION_QUICK_SCRUB
-                    ? FAST_OVERVIEW : OVERVIEW;
-            if (wasVisible) {
-                DeviceProfile dp = activity.getDeviceProfile();
-                long accuracy = 2 * Math.max(dp.widthPx, dp.heightPx);
-                callback.accept(activity.getStateManager()
-                        .createAnimationToNewWorkspace(startState, endState, accuracy));
-                return;
-            }
-
-            AnimatorSet anim = new AnimatorSet();
-
-            if (!activity.getDeviceProfile().isVerticalBarLayout()) {
-                AllAppsTransitionController controller = activity.getAllAppsController();
-                float scrollRange = Math.max(controller.getShiftRange(), 1);
-                float progressDelta = (transitionLength / scrollRange);
-
-                float endProgress = endState.getVerticalProgress(activity);
-                float startProgress = endProgress + progressDelta;
-                ObjectAnimator shiftAnim = ObjectAnimator.ofFloat(
-                        controller, ALL_APPS_PROGRESS, startProgress, endProgress);
-                shiftAnim.setInterpolator(LINEAR);
-                anim.play(shiftAnim);
-
-                // Since we are changing the start position of the UI, reapply the state, at the end
-                anim.addListener(new AnimationSuccessListener() {
-                    @Override
-                    public void onAnimationSuccess(Animator animator) {
-                        activity.getStateManager().reapplyState();
-                    }
-                });
-            }
-
-            if (interactionType == INTERACTION_NORMAL) {
-                playScaleDownAnim(anim, activity);
-            }
-
-            anim.setDuration(transitionLength * 2);
-            activity.getStateManager().setCurrentAnimation(anim);
-            callback.accept(AnimatorPlaybackController.wrap(anim, transitionLength * 2));
-        }
-
-        /**
-         * Scale down recents from the center task being full screen to being in overview.
-         */
-        private void playScaleDownAnim(AnimatorSet anim, Launcher launcher) {
-            RecentsView recentsView = launcher.getOverviewPanel();
-            TaskView v = recentsView.getTaskViewAt(recentsView.getCurrentPage());
-            if (v == null) {
-                return;
-            }
-            ClipAnimationHelper clipHelper = new ClipAnimationHelper();
-            clipHelper.fromTaskThumbnailView(v.getThumbnail(), (RecentsView) v.getParent(), null);
-            if (!clipHelper.getSourceRect().isEmpty() && !clipHelper.getTargetRect().isEmpty()) {
-                float fromScale = clipHelper.getSourceRect().width()
-                        / clipHelper.getTargetRect().width();
-                float fromTranslationY = clipHelper.getSourceRect().centerY()
-                        - clipHelper.getTargetRect().centerY();
-                Animator scale = ObjectAnimator.ofFloat(recentsView, SCALE_PROPERTY, fromScale, 1);
-                Animator translateY = ObjectAnimator.ofFloat(recentsView, TRANSLATION_Y,
-                        fromTranslationY, 0);
-                scale.setInterpolator(LINEAR);
-                translateY.setInterpolator(LINEAR);
-                anim.playTogether(scale, translateY);
-            }
-        }
-
-        @Override
-        public ActivityInitListener createActivityInitListener(
-                BiPredicate<Launcher, Boolean> onInitListener) {
-            return new LauncherInitListener(onInitListener);
-        }
-
-        @Nullable
-        @Override
-        public Launcher getCreatedActivity() {
-            LauncherAppState app = LauncherAppState.getInstanceNoCreate();
-            if (app == null) {
-                return null;
-            }
-            return (Launcher) app.getModel().getCallback();
-        }
-
-        @Nullable
-        @UiThread
-        private Launcher getVisibleLaucher() {
-            Launcher launcher = getCreatedActivity();
-            return (launcher != null) && launcher.isStarted() && launcher.hasWindowFocus() ?
-                    launcher : null;
-        }
-
-        @Nullable
-        @Override
-        public RecentsView getVisibleRecentsView() {
-            Launcher launcher = getVisibleLaucher();
-            return launcher != null && launcher.getStateManager().getState().overviewUi
-                    ? launcher.getOverviewPanel() : null;
-        }
-
-        @Override
-        public boolean switchToRecentsIfVisible(boolean fromRecentsButton) {
-            Launcher launcher = getVisibleLaucher();
-            if (launcher != null) {
-                if (fromRecentsButton) {
-                    launcher.getUserEventDispatcher().logActionCommand(
-                            LauncherLogProto.Action.Command.RECENTS_BUTTON,
-                            getContainerType(),
-                            LauncherLogProto.ContainerType.TASKSWITCHER);
-                }
-                launcher.getStateManager().goToState(OVERVIEW);
-                return true;
-            }
-            return false;
-        }
-
-        @Override
-        public boolean deferStartingActivity(int downHitTarget) {
-            return downHitTarget == HIT_TARGET_BACK || downHitTarget == HIT_TARGET_ROTATION;
-        }
-
-        @Override
-        public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target) {
-            return homeBounds;
-        }
-
-        @Override
-        public boolean shouldMinimizeSplitScreen() {
-            return true;
-        }
-
-        @Override
-        public boolean supportsLongSwipe(Launcher activity) {
-            return !activity.getDeviceProfile().isVerticalBarLayout();
-        }
-
-        @Override
-        public LongSwipeHelper getLongSwipeController(Launcher activity,
-                RemoteAnimationTargetSet targetSet) {
-            if (activity.getDeviceProfile().isVerticalBarLayout()) {
-                return null;
-            }
-            return new LongSwipeHelper(activity, targetSet);
-        }
-
-        @Override
-        public AlphaProperty getAlphaProperty(Launcher activity) {
-            return activity.getDragLayer().getAlphaProperty(DragLayer.ALPHA_INDEX_SWIPE_UP);
-        }
-
-        @Override
-        public int getContainerType() {
-            final Launcher launcher = getVisibleLaucher();
-            return launcher != null ? launcher.getStateManager().getState().containerType
-                    : LauncherLogProto.ContainerType.APP;
-        }
-    }
-
-    class FallbackActivityControllerHelper implements ActivityControlHelper<RecentsActivity> {
-
-        private final ComponentName mHomeComponent;
-        private final Handler mUiHandler = new Handler(Looper.getMainLooper());
-
-        public FallbackActivityControllerHelper(ComponentName homeComponent) {
-            mHomeComponent = homeComponent;
-        }
-
-        @Override
-        public void onQuickInteractionStart(RecentsActivity activity, RunningTaskInfo taskInfo,
-                boolean activityVisible) {
-            QuickScrubController controller = activity.<RecentsView>getOverviewPanel()
-                    .getQuickScrubController();
-
-            // TODO: match user is as well
-            boolean startingFromHome = !activityVisible &&
-                    (taskInfo == null || Objects.equals(taskInfo.topActivity, mHomeComponent));
-            controller.onQuickScrubStart(startingFromHome, this);
-            if (activityVisible) {
-                mUiHandler.postDelayed(controller::onFinishedTransitionToQuickScrub,
-                        OVERVIEW_TRANSITION_MS);
-            }
-        }
-
-        @Override
-        public float getTranslationYForQuickScrub(TransformedRect targetRect, DeviceProfile dp,
-                Context context) {
-            return 0;
-        }
-
-        @Override
-        public void executeOnWindowAvailable(RecentsActivity activity, Runnable action) {
-            action.run();
-        }
-
-        @Override
-        public void onTransitionCancelled(RecentsActivity activity, boolean activityVisible) {
-            // TODO:
-        }
-
-        @Override
-        public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context,
-                @InteractionType int interactionType, TransformedRect outRect) {
-            LayoutUtils.calculateFallbackTaskSize(context, dp, outRect.rect);
-            if (dp.isVerticalBarLayout()) {
-                Rect targetInsets = dp.getInsets();
-                int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right;
-                return dp.hotseatBarSizePx + hotseatInset;
-            } else {
-                return dp.heightPx - outRect.rect.bottom;
-            }
-        }
-
-        @Override
-        public void onSwipeUpComplete(RecentsActivity activity) {
-            // TODO:
-        }
-
-        @Override
-        public AnimationFactory prepareRecentsUI(RecentsActivity activity, boolean activityVisible,
-                Consumer<AnimatorPlaybackController> callback) {
-            if (activityVisible) {
-                return (transitionLength, interactionType) -> { };
-            }
-
-            RecentsView rv = activity.getOverviewPanel();
-            rv.setContentAlpha(0);
-
-            return new AnimationFactory() {
-
-                boolean isAnimatingHome = false;
-
-                @Override
-                public void onRemoteAnimationReceived(RemoteAnimationTargetSet targets) {
-                    isAnimatingHome = targets != null && targets.isAnimatingHome();
-                    if (!isAnimatingHome) {
-                        rv.setContentAlpha(1);
-                    }
-                    createActivityController(getSwipeUpDestinationAndLength(
-                            activity.getDeviceProfile(), activity, INTERACTION_NORMAL,
-                            new TransformedRect()), INTERACTION_NORMAL);
-                }
-
-                @Override
-                public void createActivityController(long transitionLength, int interactionType) {
-                    if (!isAnimatingHome) {
-                        return;
-                    }
-
-                    ObjectAnimator anim = ObjectAnimator.ofFloat(rv, CONTENT_ALPHA, 0, 1);
-                    anim.setDuration(transitionLength).setInterpolator(LINEAR);
-                    AnimatorSet animatorSet = new AnimatorSet();
-                    animatorSet.play(anim);
-                    callback.accept(AnimatorPlaybackController.wrap(animatorSet, transitionLength));
-                }
-            };
-        }
-
-        @Override
-        public LayoutListener createLayoutListener(RecentsActivity activity) {
-            // We do not change anything as part of layout changes in fallback activity. Return a
-            // default layout listener.
-            return new LayoutListener() {
-                @Override
-                public void open() { }
-
-                @Override
-                public void setHandler(WindowTransformSwipeHandler handler) { }
-
-                @Override
-                public void finish() { }
-            };
-        }
-
-        @Override
-        public ActivityInitListener createActivityInitListener(
-                BiPredicate<RecentsActivity, Boolean> onInitListener) {
-            return new RecentsActivityTracker(onInitListener);
-        }
-
-        @Nullable
-        @Override
-        public RecentsActivity getCreatedActivity() {
-            return RecentsActivityTracker.getCurrentActivity();
-        }
-
-        @Nullable
-        @Override
-        public RecentsView getVisibleRecentsView() {
-            RecentsActivity activity = getCreatedActivity();
-            if (activity != null && activity.hasWindowFocus()) {
-                return activity.getOverviewPanel();
-            }
-            return null;
-        }
-
-        @Override
-        public boolean switchToRecentsIfVisible(boolean fromRecentsButton) {
-            return false;
-        }
-
-        @Override
-        public boolean deferStartingActivity(int downHitTarget) {
-            // Always defer starting the activity when using fallback
-            return true;
-        }
-
-        @Override
-        public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target) {
-            // TODO: Remove this once b/77875376 is fixed
-            return target.sourceContainerBounds;
-        }
-
-        @Override
-        public boolean shouldMinimizeSplitScreen() {
-            // TODO: Remove this once b/77875376 is fixed
-            return false;
-        }
-
-        @Override
-        public boolean supportsLongSwipe(RecentsActivity activity) {
-            return false;
-        }
-
-        @Override
-        public LongSwipeHelper getLongSwipeController(RecentsActivity activity,
-                RemoteAnimationTargetSet targetSet) {
-            return null;
-        }
-
-        @Override
-        public AlphaProperty getAlphaProperty(RecentsActivity activity) {
-            return activity.getDragLayer().getAlphaProperty(0);
-        }
-
-        @Override
-        public int getContainerType() {
-            return LauncherLogProto.ContainerType.SIDELOADED_LAUNCHER;
-        }
-    }
-
-    interface LayoutListener {
-
-        void open();
-
-        void setHandler(WindowTransformSwipeHandler handler);
-
-        void finish();
-    }
+    void onLaunchTaskSuccess(T activity);
 
     interface ActivityInitListener {
 
@@ -606,10 +109,48 @@
 
     interface AnimationFactory {
 
+        enum ShelfAnimState {
+            HIDE(true), PEEK(true), OVERVIEW(false), CANCEL(false);
+
+            ShelfAnimState(boolean shouldPreformHaptic) {
+                this.shouldPreformHaptic = shouldPreformHaptic;
+            }
+
+            public final boolean shouldPreformHaptic;
+        }
+
         default void onRemoteAnimationReceived(RemoteAnimationTargetSet targets) { }
 
-        void createActivityController(long transitionLength, @InteractionType int interactionType);
+        void createActivityController(long transitionLength);
+
+        default void adjustActivityControllerInterpolators() { }
 
         default void onTransitionCancelled() { }
+
+        default void setShelfState(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) { }
+    }
+
+    interface HomeAnimationFactory {
+
+        /** Return the floating view that will animate in sync with the closing window. */
+        default @Nullable View getFloatingView() {
+            return null;
+        }
+
+        @NonNull RectF getWindowTargetRect();
+
+        @NonNull AnimatorPlaybackController createActivityAnimationToHome();
+
+        default void playAtomicAnimation(float velocity) {
+            // No-op
+        }
     }
 }
diff --git a/quickstep/src/com/android/quickstep/BaseRecentsActivity.java b/quickstep/src/com/android/quickstep/BaseRecentsActivity.java
new file mode 100644
index 0000000..c840132
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/BaseRecentsActivity.java
@@ -0,0 +1,162 @@
+/*
+ * 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.content.pm.ActivityInfo.CONFIG_ORIENTATION;
+import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
+
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.os.Bundle;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.R;
+import com.android.launcher3.uioverrides.UiFactory;
+import com.android.launcher3.util.SystemUiController;
+import com.android.launcher3.util.Themes;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * A base fallback recents activity that provides support for device profile changes, activity
+ * lifecycle tracking, and basic input handling from recents.
+ *
+ * This class is only used as a fallback in case the default launcher does not have a recents
+ * implementation.
+ */
+public abstract class BaseRecentsActivity extends BaseDraggingActivity {
+
+    private Configuration mOldConfig;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mOldConfig = new Configuration(getResources().getConfiguration());
+        initDeviceProfile();
+        initViews();
+
+        getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW,
+                Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText));
+        RecentsActivityTracker.onRecentsActivityCreate(this);
+    }
+
+    /**
+     * Init drag layer and overview panel views.
+     */
+    abstract protected void initViews();
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        int diff = newConfig.diff(mOldConfig);
+        if ((diff & (CONFIG_ORIENTATION | CONFIG_SCREEN_SIZE)) != 0) {
+            onHandleConfigChanged();
+        }
+        mOldConfig.setTo(newConfig);
+        super.onConfigurationChanged(newConfig);
+    }
+
+    /**
+     * Logic for when device configuration changes (rotation, screen size change, multi-window,
+     * etc.)
+     */
+    protected void onHandleConfigChanged() {
+        mUserEventDispatcher = null;
+        initDeviceProfile();
+
+        AbstractFloatingView.closeOpenViews(this, true,
+                AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
+        dispatchDeviceProfileChanged();
+
+        reapplyUi();
+    }
+
+    /**
+     * Initialize/update the device profile.
+     */
+    private void initDeviceProfile() {
+        mDeviceProfile = createDeviceProfile();
+        onDeviceProfileInitiated();
+    }
+
+    /**
+     * Generate the device profile to use in this activity.
+     * @return device profile
+     */
+    protected DeviceProfile createDeviceProfile() {
+        DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(this).getDeviceProfile(this);
+
+        // In case we are reusing IDP, create a copy so that we don't conflict with Launcher
+        // activity.
+        return dp.copy(this);
+    }
+
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+
+        // Workaround for b/78520668, explicitly trim memory once UI is hidden
+        onTrimMemory(TRIM_MEMORY_UI_HIDDEN);
+    }
+
+    @Override
+    public void onEnterAnimationComplete() {
+        super.onEnterAnimationComplete();
+        UiFactory.onEnterAnimationComplete(this);
+    }
+
+    @Override
+    public void onTrimMemory(int level) {
+        super.onTrimMemory(level);
+        UiFactory.onTrimMemory(this, level);
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        super.onNewIntent(intent);
+        RecentsActivityTracker.onRecentsActivityNewIntent(this);
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        RecentsActivityTracker.onRecentsActivityDestroy(this);
+    }
+
+    @Override
+    public void onBackPressed() {
+        // TODO: Launch the task we came from
+        startHome();
+    }
+
+    public void startHome() {
+        startActivity(new Intent(Intent.ACTION_MAIN)
+                .addCategory(Intent.CATEGORY_HOME)
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+    }
+
+    @Override
+    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+        super.dump(prefix, fd, writer, args);
+        writer.println(prefix + "Misc:");
+        dumpMisc(writer);
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/DeferredTouchConsumer.java b/quickstep/src/com/android/quickstep/DeferredTouchConsumer.java
deleted file mode 100644
index 8e83bd0..0000000
--- a/quickstep/src/com/android/quickstep/DeferredTouchConsumer.java
+++ /dev/null
@@ -1,114 +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 android.annotation.TargetApi;
-import android.os.Build;
-import android.view.Choreographer;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-
-/**
- * A TouchConsumer which defers all events on the UIThread until the consumer is created.
- */
-@TargetApi(Build.VERSION_CODES.P)
-public class DeferredTouchConsumer implements TouchConsumer {
-
-    private final VelocityTracker mVelocityTracker;
-    private final DeferredTouchProvider mTouchProvider;
-
-    private MotionEventQueue mMyQueue;
-    private TouchConsumer mTarget;
-
-    public DeferredTouchConsumer(DeferredTouchProvider touchProvider) {
-        mVelocityTracker = VelocityTracker.obtain();
-        mTouchProvider = touchProvider;
-    }
-
-    @Override
-    public void accept(MotionEvent event) {
-        mTarget.accept(event);
-    }
-
-    @Override
-    public void reset() {
-        mTarget.reset();
-    }
-
-    @Override
-    public void updateTouchTracking(int interactionType) {
-        mTarget.updateTouchTracking(interactionType);
-    }
-
-    @Override
-    public void onQuickScrubEnd() {
-        mTarget.onQuickScrubEnd();
-    }
-
-    @Override
-    public void onQuickScrubProgress(float progress) {
-        mTarget.onQuickScrubProgress(progress);
-    }
-
-    @Override
-    public void onQuickStep(MotionEvent ev) {
-        mTarget.onQuickStep(ev);
-    }
-
-    @Override
-    public void onCommand(int command) {
-        mTarget.onCommand(command);
-    }
-
-    @Override
-    public void preProcessMotionEvent(MotionEvent ev) {
-        mVelocityTracker.addMovement(ev);
-    }
-
-    @Override
-    public Choreographer getIntrimChoreographer(MotionEventQueue queue) {
-        mMyQueue = queue;
-        return null;
-    }
-
-    @Override
-    public void deferInit() {
-        mTarget = mTouchProvider.createTouchConsumer(mVelocityTracker);
-        mTarget.getIntrimChoreographer(mMyQueue);
-    }
-
-    @Override
-    public boolean forceToLauncherConsumer() {
-        return mTarget.forceToLauncherConsumer();
-    }
-
-    @Override
-    public boolean deferNextEventToMainThread() {
-        // If our target is still null, defer the next target as well
-        TouchConsumer target = mTarget;
-        return target == null ? true : target.deferNextEventToMainThread();
-    }
-
-    @Override
-    public void onShowOverviewFromAltTab() {
-        mTarget.onShowOverviewFromAltTab();
-    }
-
-    public interface DeferredTouchProvider {
-
-        TouchConsumer createTouchConsumer(VelocityTracker tracker);
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/InstantAppResolverImpl.java b/quickstep/src/com/android/quickstep/InstantAppResolverImpl.java
index 12757c0..3e9872a 100644
--- a/quickstep/src/com/android/quickstep/InstantAppResolverImpl.java
+++ b/quickstep/src/com/android/quickstep/InstantAppResolverImpl.java
@@ -19,16 +19,11 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.InstantAppInfo;
 import android.content.pm.PackageManager;
-import android.util.Log;
 
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.util.InstantAppResolver;
 
-import java.util.ArrayList;
-import java.util.List;
-
 /**
  * Implementation of InstantAppResolver using platform APIs
  */
@@ -40,8 +35,7 @@
 
     private final PackageManager mPM;
 
-    public InstantAppResolverImpl(Context context)
-            throws NoSuchMethodException, ClassNotFoundException {
+    public InstantAppResolverImpl(Context context) {
         mPM = context.getPackageManager();
     }
 
@@ -55,23 +49,4 @@
         ComponentName cn = info.getTargetComponent();
         return cn != null && cn.getClassName().equals(COMPONENT_CLASS_MARKER);
     }
-
-    @Override
-    public List<ApplicationInfo> getInstantApps() {
-        try {
-            List<ApplicationInfo> result = new ArrayList<>();
-            for (InstantAppInfo iai : mPM.getInstantApps()) {
-                ApplicationInfo info = iai.getApplicationInfo();
-                if (info != null) {
-                    result.add(info);
-                }
-            }
-            return result;
-        } catch (SecurityException se) {
-            Log.w(TAG, "getInstantApps failed. Launcher may not be the default home app.", se);
-        } catch (Exception e) {
-            Log.e(TAG, "Error calling API: getInstantApps", e);
-        }
-        return super.getInstantApps();
-    }
 }
diff --git a/quickstep/src/com/android/quickstep/LauncherSearchIndexablesProvider.java b/quickstep/src/com/android/quickstep/LauncherSearchIndexablesProvider.java
index f5e1f6e..5d6fbe2 100644
--- a/quickstep/src/com/android/quickstep/LauncherSearchIndexablesProvider.java
+++ b/quickstep/src/com/android/quickstep/LauncherSearchIndexablesProvider.java
@@ -29,7 +29,6 @@
 import android.util.Xml;
 
 import com.android.launcher3.R;
-import com.android.launcher3.graphics.IconShapeOverride;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -88,8 +87,6 @@
             } catch (IOException |XmlPullParserException e) {
                 throw new RuntimeException(e);
             }
-        } else if (!IconShapeOverride.isSupported(getContext())) {
-            cursor.addRow(new String[] {IconShapeOverride.KEY_PREFERENCE});
         }
         return cursor;
     }
diff --git a/quickstep/src/com/android/quickstep/LongSwipeHelper.java b/quickstep/src/com/android/quickstep/LongSwipeHelper.java
deleted file mode 100644
index 6b66ec8..0000000
--- a/quickstep/src/com/android/quickstep/LongSwipeHelper.java
+++ /dev/null
@@ -1,176 +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.LauncherAnimUtils.MIN_PROGRESS_TO_ALL_APPS;
-import static com.android.launcher3.LauncherState.ALL_APPS;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.anim.Interpolators.DEACCEL;
-import static com.android.quickstep.WindowTransformSwipeHandler.MAX_SWIPE_DURATION;
-import static com.android.quickstep.WindowTransformSwipeHandler.MIN_OVERSHOOT_DURATION;
-
-import android.animation.ValueAnimator;
-import android.view.animation.Interpolator;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAnimUtils;
-import com.android.launcher3.LauncherStateManager;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.AllAppsTransitionController;
-import com.android.launcher3.allapps.DiscoveryBounce;
-import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.anim.AnimatorSetBuilder;
-import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.anim.Interpolators.OvershootParams;
-import com.android.launcher3.uioverrides.PortraitStatesTouchController;
-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.quickstep.util.RemoteAnimationTargetSet;
-import com.android.quickstep.views.RecentsView;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-
-/**
- * Utility class to handle long swipe from an app.
- * This assumes the presence of Launcher activity as long swipe is not supported on the
- * fallback activity.
- */
-public class LongSwipeHelper {
-
-    private static final float SWIPE_DURATION_MULTIPLIER =
-            Math.min(1 / MIN_PROGRESS_TO_ALL_APPS, 1 / (1 - MIN_PROGRESS_TO_ALL_APPS));
-
-    private final Launcher mLauncher;
-    private final RemoteAnimationTargetSet mTargetSet;
-
-    private float mMaxSwipeDistance = 1;
-    private AnimatorPlaybackController mAnimator;
-    private FlingBlockCheck mFlingBlockCheck = new FlingBlockCheck();
-
-    LongSwipeHelper(Launcher launcher, RemoteAnimationTargetSet targetSet) {
-        mLauncher = launcher;
-        mTargetSet = targetSet;
-        init();
-    }
-
-    private void init() {
-        mFlingBlockCheck.blockFling();
-
-        // Init animations
-        AllAppsTransitionController controller = mLauncher.getAllAppsController();
-        // TODO: Scale it down so that we can reach all-apps in screen space
-        mMaxSwipeDistance = Math.max(1, controller.getProgress() * controller.getShiftRange());
-
-        AnimatorSetBuilder builder = PortraitStatesTouchController.getOverviewToAllAppsAnimation();
-        mAnimator = mLauncher.getStateManager().createAnimationToNewWorkspace(ALL_APPS, builder,
-                Math.round(2 * mMaxSwipeDistance), null, LauncherStateManager.ANIM_ALL);
-        mAnimator.dispatchOnStart();
-    }
-
-    public void onMove(float displacement) {
-        mAnimator.setPlayFraction(displacement / mMaxSwipeDistance);
-        mFlingBlockCheck.onEvent();
-    }
-
-    public void destroy() {
-        // TODO: We can probably also show the task view
-
-        mLauncher.getStateManager().goToState(OVERVIEW, false);
-    }
-
-    public void end(float velocity, boolean isFling, Runnable callback) {
-        float velocityPxPerMs = velocity / 1000;
-        long duration = MAX_SWIPE_DURATION;
-        Interpolator interpolator = DEACCEL;
-
-        final float currentFraction = mAnimator.getProgressFraction();
-        final boolean toAllApps;
-        float endProgress;
-
-        boolean blockedFling = isFling && mFlingBlockCheck.isBlocked();
-        if (blockedFling) {
-            isFling = false;
-        }
-
-        if (!isFling) {
-            toAllApps = currentFraction > MIN_PROGRESS_TO_ALL_APPS;
-            endProgress = toAllApps ? 1 : 0;
-
-            long expectedDuration = Math.abs(Math.round((endProgress - currentFraction)
-                    * MAX_SWIPE_DURATION * SWIPE_DURATION_MULTIPLIER));
-            duration = Math.min(MAX_SWIPE_DURATION, expectedDuration);
-
-            if (blockedFling && !toAllApps) {
-                Interpolators.OvershootParams overshoot = new OvershootParams(currentFraction,
-                        currentFraction, endProgress, velocityPxPerMs, (int) mMaxSwipeDistance);
-                duration = (overshoot.duration + duration);
-                duration = Utilities.boundToRange(duration, MIN_OVERSHOOT_DURATION,
-                        MAX_SWIPE_DURATION);
-                interpolator = overshoot.interpolator;
-                endProgress = overshoot.end;
-            }
-        } else {
-            toAllApps = velocity < 0;
-            endProgress = toAllApps ? 1 : 0;
-
-            float minFlingVelocity = mLauncher.getResources()
-                    .getDimension(R.dimen.quickstep_fling_min_velocity);
-            if (Math.abs(velocity) > minFlingVelocity && mMaxSwipeDistance > 0) {
-                float distanceToTravel = (endProgress - currentFraction) * mMaxSwipeDistance;
-
-                // 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));
-                duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration);
-            }
-        }
-
-        final boolean finalIsFling = isFling;
-        mAnimator.setEndAction(() -> onSwipeAnimationComplete(toAllApps, finalIsFling, callback));
-
-        ValueAnimator animator = mAnimator.getAnimationPlayer();
-        animator.setDuration(duration).setInterpolator(interpolator);
-        animator.setFloatValues(currentFraction, endProgress);
-        animator.start();
-    }
-
-    private void onSwipeAnimationComplete(boolean toAllApps, boolean isFling, Runnable callback) {
-        mLauncher.getStateManager().goToState(toAllApps ? ALL_APPS : OVERVIEW, false);
-        if (!toAllApps) {
-            DiscoveryBounce.showForOverviewIfNeeded(mLauncher);
-            mLauncher.<RecentsView>getOverviewPanel().setSwipeDownShouldLaunchApp(true);
-        }
-
-        mLauncher.getUserEventDispatcher().logStateChangeAction(
-                isFling ? Touch.FLING : Touch.SWIPE, Direction.UP,
-                ContainerType.NAVBAR, ContainerType.APP,
-                toAllApps ? ContainerType.ALLAPPS : ContainerType.TASKSWITCHER,
-                0);
-
-        callback.run();
-    }
-
-    public float getTargetAlpha(RemoteAnimationTargetCompat app, Float expectedAlpha) {
-        if (!(app.isNotInRecents
-                || app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME)) {
-            return 0;
-        }
-        return expectedAlpha;
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/MotionEventQueue.java b/quickstep/src/com/android/quickstep/MotionEventQueue.java
deleted file mode 100644
index f73be6c..0000000
--- a/quickstep/src/com/android/quickstep/MotionEventQueue.java
+++ /dev/null
@@ -1,248 +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;
-
-import static android.view.MotionEvent.ACTION_CANCEL;
-import static android.view.MotionEvent.ACTION_MASK;
-import static android.view.MotionEvent.ACTION_MOVE;
-import static android.view.MotionEvent.ACTION_POINTER_INDEX_SHIFT;
-import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
-
-import android.annotation.TargetApi;
-import android.os.Build;
-import android.util.Log;
-import android.view.Choreographer;
-import android.view.MotionEvent;
-
-import com.android.systemui.shared.system.ChoreographerCompat;
-
-import java.util.ArrayList;
-
-/**
- * Helper class for batching input events
- */
-@TargetApi(Build.VERSION_CODES.O)
-public class MotionEventQueue {
-
-    private static final String TAG = "MotionEventQueue";
-
-    private static final int ACTION_VIRTUAL = ACTION_MASK - 1;
-
-    private static final int ACTION_QUICK_SCRUB_START =
-            ACTION_VIRTUAL | (1 << ACTION_POINTER_INDEX_SHIFT);
-    private static final int ACTION_QUICK_SCRUB_PROGRESS =
-            ACTION_VIRTUAL | (2 << ACTION_POINTER_INDEX_SHIFT);
-    private static final int ACTION_QUICK_SCRUB_END =
-            ACTION_VIRTUAL | (3 << ACTION_POINTER_INDEX_SHIFT);
-    private static final int ACTION_RESET =
-            ACTION_VIRTUAL | (4 << ACTION_POINTER_INDEX_SHIFT);
-    private static final int ACTION_DEFER_INIT =
-            ACTION_VIRTUAL | (5 << ACTION_POINTER_INDEX_SHIFT);
-    private static final int ACTION_SHOW_OVERVIEW_FROM_ALT_TAB =
-            ACTION_VIRTUAL | (6 << ACTION_POINTER_INDEX_SHIFT);
-    private static final int ACTION_QUICK_STEP =
-            ACTION_VIRTUAL | (7 << ACTION_POINTER_INDEX_SHIFT);
-    private static final int ACTION_COMMAND =
-            ACTION_VIRTUAL | (8 << ACTION_POINTER_INDEX_SHIFT);
-
-    private final EventArray mEmptyArray = new EventArray();
-    private final Object mExecutionLock = new Object();
-
-    // We use two arrays and swap the current index when one array is being consumed
-    private final EventArray[] mArrays = new EventArray[] {new EventArray(), new EventArray()};
-    private int mCurrentIndex = 0;
-
-    private final Runnable mMainFrameCallback = this::frameCallbackForMainChoreographer;
-    private final Runnable mInterimFrameCallback = this::frameCallbackForInterimChoreographer;
-
-    private final Choreographer mMainChoreographer;
-
-    private final TouchConsumer mConsumer;
-
-    private Choreographer mInterimChoreographer;
-    private Choreographer mCurrentChoreographer;
-
-    private Runnable mCurrentRunnable;
-
-    public MotionEventQueue(Choreographer choreographer, TouchConsumer consumer) {
-        mMainChoreographer = choreographer;
-        mConsumer = consumer;
-        mCurrentChoreographer = mMainChoreographer;
-        mCurrentRunnable = mMainFrameCallback;
-
-        setInterimChoreographer(consumer.getIntrimChoreographer(this));
-    }
-
-    public void setInterimChoreographer(Choreographer choreographer) {
-        synchronized (mExecutionLock) {
-            synchronized (mArrays) {
-                setInterimChoreographerLocked(choreographer);
-                ChoreographerCompat.postInputFrame(mCurrentChoreographer, mCurrentRunnable);
-            }
-        }
-    }
-
-    private void  setInterimChoreographerLocked(Choreographer choreographer) {
-        mInterimChoreographer = choreographer;
-        if (choreographer == null) {
-            mCurrentChoreographer = mMainChoreographer;
-            mCurrentRunnable = mMainFrameCallback;
-        } else {
-            mCurrentChoreographer = mInterimChoreographer;
-            mCurrentRunnable = mInterimFrameCallback;
-        }
-    }
-
-    public void queue(MotionEvent event) {
-        mConsumer.preProcessMotionEvent(event);
-        queueNoPreProcess(event);
-    }
-
-    private void queueNoPreProcess(MotionEvent event) {
-        synchronized (mArrays) {
-            EventArray array = mArrays[mCurrentIndex];
-            if (array.isEmpty()) {
-                ChoreographerCompat.postInputFrame(mCurrentChoreographer, mCurrentRunnable);
-            }
-
-            int eventAction = event.getAction();
-            if (eventAction == ACTION_MOVE && array.lastEventAction == ACTION_MOVE) {
-                // Replace and recycle the last event
-                array.set(array.size() - 1, event).recycle();
-            } else {
-                array.add(event);
-                array.lastEventAction = eventAction;
-            }
-        }
-    }
-
-    private void frameCallbackForMainChoreographer() {
-        runFor(mMainChoreographer);
-    }
-
-    private void frameCallbackForInterimChoreographer() {
-        runFor(mInterimChoreographer);
-    }
-
-    private void runFor(Choreographer caller) {
-        synchronized (mExecutionLock) {
-            EventArray array = swapAndGetCurrentArray(caller);
-            int size = array.size();
-            for (int i = 0; i < size; i++) {
-                MotionEvent event = array.get(i);
-                if (event.getActionMasked() == ACTION_VIRTUAL) {
-                    switch (event.getAction()) {
-                        case ACTION_QUICK_SCRUB_START:
-                            mConsumer.updateTouchTracking(INTERACTION_QUICK_SCRUB);
-                            break;
-                        case ACTION_QUICK_SCRUB_PROGRESS:
-                            mConsumer.onQuickScrubProgress(event.getX());
-                            break;
-                        case ACTION_QUICK_SCRUB_END:
-                            mConsumer.onQuickScrubEnd();
-                            break;
-                        case ACTION_RESET:
-                            mConsumer.reset();
-                            break;
-                        case ACTION_DEFER_INIT:
-                            mConsumer.deferInit();
-                            break;
-                        case ACTION_SHOW_OVERVIEW_FROM_ALT_TAB:
-                            mConsumer.onShowOverviewFromAltTab();
-                            mConsumer.updateTouchTracking(INTERACTION_QUICK_SCRUB);
-                            break;
-                        case ACTION_QUICK_STEP:
-                            mConsumer.onQuickStep(event);
-                            break;
-                        case ACTION_COMMAND:
-                            mConsumer.onCommand(event.getSource());
-                            break;
-                        default:
-                            Log.e(TAG, "Invalid virtual event: " + event.getAction());
-                    }
-                } else {
-                    mConsumer.accept(event);
-                }
-                event.recycle();
-            }
-            array.clear();
-            array.lastEventAction = ACTION_CANCEL;
-        }
-    }
-
-    private EventArray swapAndGetCurrentArray(Choreographer caller) {
-        synchronized (mArrays) {
-            if (caller != mCurrentChoreographer) {
-                return mEmptyArray;
-            }
-            EventArray current = mArrays[mCurrentIndex];
-            mCurrentIndex = mCurrentIndex ^ 1;
-            return current;
-        }
-    }
-
-    private void queueVirtualAction(int action, float progress) {
-        queueNoPreProcess(MotionEvent.obtain(0, 0, action, progress, 0, 0));
-    }
-
-    public void onQuickScrubStart() {
-        queueVirtualAction(ACTION_QUICK_SCRUB_START, 0);
-    }
-
-    public void onOverviewShownFromAltTab() {
-        queueVirtualAction(ACTION_SHOW_OVERVIEW_FROM_ALT_TAB, 0);
-    }
-
-    public void onQuickScrubProgress(float progress) {
-        queueVirtualAction(ACTION_QUICK_SCRUB_PROGRESS, progress);
-    }
-
-    public void onQuickScrubEnd() {
-        queueVirtualAction(ACTION_QUICK_SCRUB_END, 0);
-    }
-
-    public void onQuickStep(MotionEvent event) {
-        event.setAction(ACTION_QUICK_STEP);
-        queueNoPreProcess(event);
-    }
-
-    public void reset() {
-        queueVirtualAction(ACTION_RESET, 0);
-    }
-
-    public void deferInit() {
-        queueVirtualAction(ACTION_DEFER_INIT, 0);
-    }
-
-    public void onCommand(int command) {
-        MotionEvent ev = MotionEvent.obtain(0, 0, ACTION_COMMAND, 0, 0, 0);
-        ev.setSource(command);
-        queueNoPreProcess(ev);
-    }
-
-    public TouchConsumer getConsumer() {
-        return mConsumer;
-    }
-
-    private static class EventArray extends ArrayList<MotionEvent> {
-
-        public int lastEventAction = ACTION_CANCEL;
-
-        public EventArray() {
-            super(4);
-        }
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/MultiStateCallback.java b/quickstep/src/com/android/quickstep/MultiStateCallback.java
deleted file mode 100644
index bda3d06..0000000
--- a/quickstep/src/com/android/quickstep/MultiStateCallback.java
+++ /dev/null
@@ -1,66 +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;
-
-import android.util.SparseArray;
-
-/**
- * Utility class to help manage multiple callbacks based on different states.
- */
-public class MultiStateCallback {
-
-    private final SparseArray<Runnable> mCallbacks = new SparseArray<>();
-
-    private int mState = 0;
-
-    /**
-     * Adds the provided state flags to the global state and executes any callbacks as a result.
-     * @param stateFlag
-     */
-    public void setState(int stateFlag) {
-        mState = mState | stateFlag;
-
-        int count = mCallbacks.size();
-        for (int i = 0; i < count; i++) {
-            int state = mCallbacks.keyAt(i);
-
-            if ((mState & state) == state) {
-                Runnable callback = mCallbacks.valueAt(i);
-                if (callback != null) {
-                    // Set the callback to null, so that it does not run again.
-                    mCallbacks.setValueAt(i, null);
-                    callback.run();
-                }
-            }
-        }
-    }
-
-    /**
-     * Sets the callbacks to be run when the provided states are enabled.
-     * The callback is only run once.
-     */
-    public void addCallback(int stateMask, Runnable callback) {
-        mCallbacks.put(stateMask, callback);
-    }
-
-    public int getState() {
-        return mState;
-    }
-
-    public boolean hasStates(int stateMask) {
-        return (mState & stateMask) == stateMask;
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/NormalizedIconLoader.java b/quickstep/src/com/android/quickstep/NormalizedIconLoader.java
index f875bb7..bd6204a 100644
--- a/quickstep/src/com/android/quickstep/NormalizedIconLoader.java
+++ b/quickstep/src/com/android/quickstep/NormalizedIconLoader.java
@@ -28,9 +28,9 @@
 import android.util.SparseArray;
 
 import com.android.launcher3.FastBitmapDrawable;
-import com.android.launcher3.graphics.BitmapInfo;
+import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.graphics.DrawableFactory;
-import com.android.launcher3.graphics.LauncherIcons;
+import com.android.launcher3.icons.LauncherIcons;
 import com.android.systemui.shared.recents.model.IconLoader;
 import com.android.systemui.shared.recents.model.TaskKeyLruCache;
 
@@ -42,12 +42,14 @@
 
     private final SparseArray<BitmapInfo> mDefaultIcons = new SparseArray<>();
     private final DrawableFactory mDrawableFactory;
-    private LauncherIcons mLauncherIcons;
+    private final boolean mDisableColorExtraction;
 
     public NormalizedIconLoader(Context context, TaskKeyLruCache<Drawable> iconCache,
-            LruCache<ComponentName, ActivityInfo> activityInfoCache) {
+            LruCache<ComponentName, ActivityInfo> activityInfoCache,
+            boolean disableColorExtraction) {
         super(context, iconCache, activityInfoCache);
-        mDrawableFactory = DrawableFactory.get(context);
+        mDrawableFactory = DrawableFactory.INSTANCE.get(context);
+        mDisableColorExtraction = disableColorExtraction;
     }
 
     @Override
@@ -70,16 +72,18 @@
                 false));
     }
 
-    private synchronized BitmapInfo getBitmapInfo(Drawable drawable, int userId,
+    private BitmapInfo getBitmapInfo(Drawable drawable, int userId,
             int primaryColor, boolean isInstantApp) {
-        if (mLauncherIcons == null) {
-            mLauncherIcons = LauncherIcons.obtain(mContext);
-        }
+        try (LauncherIcons la = LauncherIcons.obtain(mContext)) {
+            if (mDisableColorExtraction) {
+                la.disableColorExtraction();
+            }
+            la.setWrapperBackgroundColor(primaryColor);
 
-        mLauncherIcons.setWrapperBackgroundColor(primaryColor);
-        // User version code O, so that the icon is always wrapped in an adaptive icon container.
-        return mLauncherIcons.createBadgedIconBitmap(drawable, UserHandle.of(userId),
-                Build.VERSION_CODES.O, isInstantApp);
+            // User version code O, so that the icon is always wrapped in an adaptive icon container
+            return la.createBadgedIconBitmap(drawable, UserHandle.of(userId),
+                    Build.VERSION_CODES.O, isInstantApp);
+        }
     }
 
     @Override
@@ -90,6 +94,6 @@
                 userId,
                 desc.getPrimaryColor(),
                 activityInfo.applicationInfo.isInstantApp());
-        return mDrawableFactory.newIcon(bitmapInfo, activityInfo);
+        return mDrawableFactory.newIcon(mContext, bitmapInfo, activityInfo);
     }
 }
diff --git a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
deleted file mode 100644
index 0e811f7..0000000
--- a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
+++ /dev/null
@@ -1,451 +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 android.view.MotionEvent.ACTION_CANCEL;
-import static android.view.MotionEvent.ACTION_DOWN;
-import static android.view.MotionEvent.ACTION_MOVE;
-import static android.view.MotionEvent.ACTION_POINTER_UP;
-import static android.view.MotionEvent.ACTION_UP;
-import static android.view.MotionEvent.INVALID_POINTER_ID;
-
-import static com.android.systemui.shared.system.ActivityManagerWrapper
-        .CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
-
-import android.annotation.TargetApi;
-import android.app.ActivityManager.RunningTaskInfo;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.Intent;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Looper;
-import android.os.SystemClock;
-import android.util.SparseArray;
-import android.view.Choreographer;
-import android.view.Display;
-import android.view.MotionEvent;
-import android.view.Surface;
-import android.view.VelocityTracker;
-import android.view.ViewConfiguration;
-import android.view.WindowManager;
-
-import com.android.launcher3.MainThreadExecutor;
-import com.android.launcher3.util.TraceHelper;
-import com.android.quickstep.util.RemoteAnimationTargetSet;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.AssistDataReceiver;
-import com.android.systemui.shared.system.BackgroundExecutor;
-import com.android.systemui.shared.system.NavigationBarCompat;
-import com.android.systemui.shared.system.NavigationBarCompat.HitTarget;
-import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
-import com.android.systemui.shared.system.RecentsAnimationListener;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.WindowManagerWrapper;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Touch consumer for handling events originating from an activity other than Launcher
- */
-@TargetApi(Build.VERSION_CODES.P)
-public class OtherActivityTouchConsumer extends ContextWrapper implements TouchConsumer {
-
-    private static final long LAUNCHER_DRAW_TIMEOUT_MS = 150;
-
-    private final SparseArray<RecentsAnimationState> mAnimationStates = new SparseArray<>();
-    private final RunningTaskInfo mRunningTask;
-    private final RecentsModel mRecentsModel;
-    private final Intent mHomeIntent;
-    private final ActivityControlHelper mActivityControlHelper;
-    private final MainThreadExecutor mMainThreadExecutor;
-    private final Choreographer mBackgroundThreadChoreographer;
-    private final OverviewCallbacks mOverviewCallbacks;
-    private final TaskOverlayFactory mTaskOverlayFactory;
-
-    private final boolean mIsDeferredDownTarget;
-    private final PointF mDownPos = new PointF();
-    private final PointF mLastPos = new PointF();
-    private int mActivePointerId = INVALID_POINTER_ID;
-    private boolean mPassedInitialSlop;
-    // Used for non-deferred gestures to determine when to start dragging
-    private int mQuickStepDragSlop;
-    private float mStartDisplacement;
-    private WindowTransformSwipeHandler mInteractionHandler;
-    private int mDisplayRotation;
-    private Rect mStableInsets = new Rect();
-
-    private VelocityTracker mVelocityTracker;
-    private MotionEventQueue mEventQueue;
-    private boolean mIsGoingToHome;
-
-    public OtherActivityTouchConsumer(Context base, RunningTaskInfo runningTaskInfo,
-            RecentsModel recentsModel, Intent homeIntent, ActivityControlHelper activityControl,
-            MainThreadExecutor mainThreadExecutor, Choreographer backgroundThreadChoreographer,
-            @HitTarget int downHitTarget, OverviewCallbacks overviewCallbacks,
-            TaskOverlayFactory taskOverlayFactory, VelocityTracker velocityTracker) {
-        super(base);
-
-        mRunningTask = runningTaskInfo;
-        mRecentsModel = recentsModel;
-        mHomeIntent = homeIntent;
-        mVelocityTracker = velocityTracker;
-        mActivityControlHelper = activityControl;
-        mMainThreadExecutor = mainThreadExecutor;
-        mBackgroundThreadChoreographer = backgroundThreadChoreographer;
-        mIsDeferredDownTarget = activityControl.deferStartingActivity(downHitTarget);
-        mOverviewCallbacks = overviewCallbacks;
-        mTaskOverlayFactory = taskOverlayFactory;
-    }
-
-    @Override
-    public void onShowOverviewFromAltTab() {
-        startTouchTrackingForWindowAnimation(SystemClock.uptimeMillis());
-    }
-
-    @Override
-    public void accept(MotionEvent ev) {
-        if (mVelocityTracker == null) {
-            return;
-        }
-        switch (ev.getActionMasked()) {
-            case ACTION_DOWN: {
-                TraceHelper.beginSection("TouchInt");
-                mActivePointerId = ev.getPointerId(0);
-                mDownPos.set(ev.getX(), ev.getY());
-                mLastPos.set(mDownPos);
-                mPassedInitialSlop = false;
-                mQuickStepDragSlop = NavigationBarCompat.getQuickStepDragSlopPx();
-
-                // Start the window animation on down to give more time for launcher to draw if the
-                // user didn't start the gesture over the back button
-                if (!mIsDeferredDownTarget) {
-                    startTouchTrackingForWindowAnimation(ev.getEventTime());
-                }
-
-                Display display = getSystemService(WindowManager.class).getDefaultDisplay();
-                mDisplayRotation = display.getRotation();
-                WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
-                break;
-            }
-            case ACTION_POINTER_UP: {
-                int ptrIdx = ev.getActionIndex();
-                int ptrId = ev.getPointerId(ptrIdx);
-                if (ptrId == mActivePointerId) {
-                    final int newPointerIdx = ptrIdx == 0 ? 1 : 0;
-                    mDownPos.set(
-                            ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x),
-                            ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y));
-                    mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx));
-                    mActivePointerId = ev.getPointerId(newPointerIdx);
-                }
-                break;
-            }
-            case ACTION_MOVE: {
-                int pointerIndex = ev.findPointerIndex(mActivePointerId);
-                if (pointerIndex == INVALID_POINTER_ID) {
-                    break;
-                }
-                mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
-                float displacement = getDisplacement(ev);
-                if (!mPassedInitialSlop) {
-                    if (!mIsDeferredDownTarget) {
-                        // Normal gesture, ensure we pass the drag slop before we start tracking
-                        // the gesture
-                        if (Math.abs(displacement) > mQuickStepDragSlop) {
-                            mPassedInitialSlop = true;
-                            mStartDisplacement = displacement;
-                        }
-                    }
-                }
-
-                if (mPassedInitialSlop && mInteractionHandler != null) {
-                    // Move
-                    mInteractionHandler.updateDisplacement(displacement - mStartDisplacement);
-                }
-                break;
-            }
-            case ACTION_CANCEL:
-                // TODO: Should be different than ACTION_UP
-            case ACTION_UP: {
-                TraceHelper.endSection("TouchInt");
-
-                finishTouchTracking(ev);
-                break;
-            }
-        }
-    }
-
-    private void notifyGestureStarted() {
-        if (mInteractionHandler == null) {
-            return;
-        }
-
-        mOverviewCallbacks.closeAllWindows();
-        ActivityManagerWrapper.getInstance().closeSystemWindows(
-                CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
-
-        // Notify the handler that the gesture has actually started
-        mInteractionHandler.onGestureStarted();
-    }
-
-    private boolean isNavBarOnRight() {
-        return mDisplayRotation == Surface.ROTATION_90 && mStableInsets.right > 0;
-    }
-
-    private boolean isNavBarOnLeft() {
-        return mDisplayRotation == Surface.ROTATION_270 && mStableInsets.left > 0;
-    }
-
-    private void startTouchTrackingForWindowAnimation(long touchTimeMs) {
-        // Create the shared handler
-        RecentsAnimationState animationState = new RecentsAnimationState();
-        final WindowTransformSwipeHandler handler = new WindowTransformSwipeHandler(
-                animationState.id, mRunningTask, this, touchTimeMs, mActivityControlHelper);
-
-        // Preload the plan
-        mRecentsModel.loadTasks(mRunningTask.id, null);
-        mInteractionHandler = handler;
-        handler.setGestureEndCallback(mEventQueue::reset);
-
-        CountDownLatch drawWaitLock = new CountDownLatch(1);
-        handler.setLauncherOnDrawCallback(() -> {
-            drawWaitLock.countDown();
-            if (handler == mInteractionHandler) {
-                switchToMainChoreographer();
-            }
-        });
-        handler.initWhenReady();
-
-        TraceHelper.beginSection("RecentsController");
-
-        AssistDataReceiver assistDataReceiver = !mTaskOverlayFactory.needAssist() ? null :
-                new AssistDataReceiver() {
-                    @Override
-                    public void onHandleAssistData(Bundle bundle) {
-                        if (mInteractionHandler == null) {
-                            // Interaction is probably complete
-                            mRecentsModel.preloadAssistData(mRunningTask.id, bundle);
-                        } else if (handler == mInteractionHandler) {
-                            handler.onAssistDataReceived(bundle);
-                        }
-                    }
-                };
-
-        Runnable startActivity = () -> ActivityManagerWrapper.getInstance().startRecentsActivity(
-                mHomeIntent, assistDataReceiver, animationState, null, null);
-
-        if (Looper.myLooper() != Looper.getMainLooper()) {
-            startActivity.run();
-            try {
-                drawWaitLock.await(LAUNCHER_DRAW_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-            } catch (Exception e) {
-                // We have waited long enough for launcher to draw
-            }
-        } else {
-            // We should almost always get touch-town on background thread. This is an edge case
-            // when the background Choreographer has not yet initialized.
-            BackgroundExecutor.get().submit(startActivity);
-        }
-    }
-
-    @Override
-    public void onCommand(int command) {
-        RecentsAnimationState state = mAnimationStates.get(command);
-        if (state != null) {
-            state.execute();
-        }
-    }
-
-    /**
-     * Called when the gesture has ended. Does not correlate to the completion of the interaction as
-     * the animation can still be running.
-     */
-    private void finishTouchTracking(MotionEvent ev) {
-        if (mPassedInitialSlop && mInteractionHandler != null) {
-            mInteractionHandler.updateDisplacement(getDisplacement(ev) - mStartDisplacement);
-
-            mVelocityTracker.computeCurrentVelocity(1000,
-                    ViewConfiguration.get(this).getScaledMaximumFlingVelocity());
-
-            float velocity = isNavBarOnRight() ? mVelocityTracker.getXVelocity(mActivePointerId)
-                    : isNavBarOnLeft() ? -mVelocityTracker.getXVelocity(mActivePointerId)
-                            : mVelocityTracker.getYVelocity(mActivePointerId);
-            mInteractionHandler.onGestureEnded(velocity);
-        } else {
-            // Since we start touch tracking on DOWN, we may reach this state without actually
-            // starting the gesture. In that case, just cleanup immediately.
-            reset();
-
-            // Also clean up in case the system has handled the UP and canceled the animation before
-            // we had a chance to start the recents animation. In such a case, we will not receive
-            ActivityManagerWrapper.getInstance().cancelRecentsAnimation(
-                    true /* restoreHomeStackPosition */);
-        }
-        mVelocityTracker.recycle();
-        mVelocityTracker = null;
-    }
-
-    @Override
-    public void reset() {
-        // Clean up the old interaction handler
-        if (mInteractionHandler != null) {
-            final WindowTransformSwipeHandler handler = mInteractionHandler;
-            mInteractionHandler = null;
-            mIsGoingToHome = handler.mIsGoingToHome;
-            mMainThreadExecutor.execute(handler::reset);
-        }
-    }
-
-    @Override
-    public void updateTouchTracking(int interactionType) {
-        if (!mPassedInitialSlop && mIsDeferredDownTarget && mInteractionHandler == null) {
-            // If we deferred starting the window animation on touch down, then
-            // start tracking now
-            startTouchTrackingForWindowAnimation(SystemClock.uptimeMillis());
-            mPassedInitialSlop = true;
-        }
-
-        if (mInteractionHandler != null) {
-            mInteractionHandler.updateInteractionType(interactionType);
-        }
-        notifyGestureStarted();
-    }
-
-    @Override
-    public Choreographer getIntrimChoreographer(MotionEventQueue queue) {
-        mEventQueue = queue;
-        return mBackgroundThreadChoreographer;
-    }
-
-    @Override
-    public void onQuickScrubEnd() {
-        if (mInteractionHandler != null) {
-            mInteractionHandler.onQuickScrubEnd();
-        }
-    }
-
-    @Override
-    public void onQuickScrubProgress(float progress) {
-        if (mInteractionHandler != null) {
-            mInteractionHandler.onQuickScrubProgress(progress);
-        }
-    }
-
-    @Override
-    public void onQuickStep(MotionEvent ev) {
-        if (mIsDeferredDownTarget) {
-            // Deferred gesture, start the animation and gesture tracking once we pass the actual
-            // touch slop
-            startTouchTrackingForWindowAnimation(ev.getEventTime());
-            mPassedInitialSlop = true;
-            mStartDisplacement = getDisplacement(ev);
-        }
-        notifyGestureStarted();
-    }
-
-    private float getDisplacement(MotionEvent ev) {
-        float eventX = ev.getX();
-        float eventY = ev.getY();
-        float displacement = eventY - mDownPos.y;
-        if (isNavBarOnRight()) {
-            displacement = eventX - mDownPos.x;
-        } else if (isNavBarOnLeft()) {
-            displacement = mDownPos.x - eventX;
-        }
-        return displacement;
-    }
-
-    public void switchToMainChoreographer() {
-        mEventQueue.setInterimChoreographer(null);
-    }
-
-    @Override
-    public void preProcessMotionEvent(MotionEvent ev) {
-        if (mVelocityTracker != null) {
-           mVelocityTracker.addMovement(ev);
-           if (ev.getActionMasked() == ACTION_POINTER_UP) {
-               mVelocityTracker.clear();
-           }
-        }
-    }
-
-    @Override
-    public boolean forceToLauncherConsumer() {
-        return mIsGoingToHome;
-    }
-
-    @Override
-    public boolean deferNextEventToMainThread() {
-        // TODO: Consider also check if the eventQueue is using mainThread of not.
-        return mInteractionHandler != null;
-    }
-
-    private class RecentsAnimationState implements RecentsAnimationListener {
-
-        private final int id;
-
-        private RecentsAnimationControllerCompat mController;
-        private RemoteAnimationTargetSet mTargets;
-        private Rect mHomeContentInsets;
-        private Rect mMinimizedHomeBounds;
-        private boolean mCancelled;
-
-        public RecentsAnimationState() {
-            id = mAnimationStates.size();
-            mAnimationStates.put(id, this);
-        }
-
-        @Override
-        public void onAnimationStart(
-                RecentsAnimationControllerCompat controller,
-                RemoteAnimationTargetCompat[] apps, Rect homeContentInsets,
-                Rect minimizedHomeBounds) {
-            mController = controller;
-            mTargets = new RemoteAnimationTargetSet(apps, MODE_CLOSING);
-            mHomeContentInsets = homeContentInsets;
-            mMinimizedHomeBounds = minimizedHomeBounds;
-            mEventQueue.onCommand(id);
-        }
-
-        @Override
-        public void onAnimationCanceled() {
-            mCancelled = true;
-            mEventQueue.onCommand(id);
-        }
-
-        public void execute() {
-            if (mInteractionHandler == null || mInteractionHandler.id != id) {
-                if (!mCancelled && mController != null) {
-                    TraceHelper.endSection("RecentsController", "Finishing no handler");
-                    mController.finish(false /* toHome */);
-                }
-            } else if (mCancelled) {
-                TraceHelper.endSection("RecentsController",
-                        "Cancelled: " + mInteractionHandler);
-                mInteractionHandler.onRecentsAnimationCanceled();
-            } else {
-                TraceHelper.partitionSection("RecentsController", "Received");
-                mInteractionHandler.onRecentsAnimationStart(mController, mTargets,
-                        mHomeContentInsets, mMinimizedHomeBounds);
-            }
-        }
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/OverviewCallbacks.java b/quickstep/src/com/android/quickstep/OverviewCallbacks.java
index ac4a40b..f5573ba 100644
--- a/quickstep/src/com/android/quickstep/OverviewCallbacks.java
+++ b/quickstep/src/com/android/quickstep/OverviewCallbacks.java
@@ -18,20 +18,20 @@
 import android.content.Context;
 
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.util.Preconditions;
+import com.android.launcher3.util.ResourceBasedOverride;
 
 /**
  * Callbacks related to overview/quicksteps.
  */
-public class OverviewCallbacks {
+public class OverviewCallbacks implements ResourceBasedOverride {
 
     private static OverviewCallbacks sInstance;
 
     public static OverviewCallbacks get(Context context) {
         Preconditions.assertUIThread();
         if (sInstance == null) {
-            sInstance = Utilities.getOverrideObject(OverviewCallbacks.class,
+            sInstance = Overrides.getObject(OverviewCallbacks.class,
                     context.getApplicationContext(), R.string.overview_callbacks_class);
         }
         return sInstance;
@@ -39,7 +39,5 @@
 
     public void onInitOverviewTransition() { }
 
-    public void onResetOverview() { }
-
     public void closeAllWindows() { }
 }
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
deleted file mode 100644
index eff94fc..0000000
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ /dev/null
@@ -1,380 +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 android.content.Intent.ACTION_PACKAGE_ADDED;
-import static android.content.Intent.ACTION_PACKAGE_CHANGED;
-import static android.content.Intent.ACTION_PACKAGE_REMOVED;
-
-import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
-import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
-import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
-import static com.android.systemui.shared.system.ActivityManagerWrapper
-        .CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
-import static com.android.systemui.shared.system.PackageManagerWrapper
-        .ACTION_PREFERRED_ACTIVITY_CHANGED;
-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.AnimatorSet;
-import android.animation.ValueAnimator;
-import android.annotation.TargetApi;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ResolveInfo;
-import android.graphics.Rect;
-import android.os.Build;
-import android.os.PatternMatcher;
-import android.os.SystemClock;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewConfiguration;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.MainThreadExecutor;
-import com.android.launcher3.anim.AnimationSuccessListener;
-import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
-import com.android.quickstep.ActivityControlHelper.AnimationFactory;
-import com.android.quickstep.ActivityControlHelper.FallbackActivityControllerHelper;
-import com.android.quickstep.ActivityControlHelper.LauncherActivityControllerHelper;
-import com.android.quickstep.util.ClipAnimationHelper;
-import com.android.quickstep.util.TransformedRect;
-import com.android.quickstep.util.RemoteAnimationTargetSet;
-import com.android.quickstep.views.RecentsView;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.LatencyTrackerCompat;
-import com.android.systemui.shared.system.PackageManagerWrapper;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier;
-import com.android.systemui.shared.system.TransactionCompat;
-
-import java.util.ArrayList;
-
-/**
- * Helper class to handle various atomic commands for switching between Overview.
- */
-@TargetApi(Build.VERSION_CODES.P)
-public class OverviewCommandHelper {
-
-    private static final long RECENTS_LAUNCH_DURATION = 250;
-
-    private static final String TAG = "OverviewCommandHelper";
-
-    private final Context mContext;
-    private final ActivityManagerWrapper mAM;
-    private final RecentsModel mRecentsModel;
-    private final MainThreadExecutor mMainThreadExecutor;
-    private final ComponentName mMyHomeComponent;
-
-    private final BroadcastReceiver mUserPreferenceChangeReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            initOverviewTargets();
-        }
-    };
-    private final BroadcastReceiver mOtherHomeAppUpdateReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            initOverviewTargets();
-        }
-    };
-    private String mUpdateRegisteredPackage;
-
-    public Intent overviewIntent;
-    public ComponentName overviewComponent;
-    private ActivityControlHelper mActivityControlHelper;
-
-    private long mLastToggleTime;
-
-    public OverviewCommandHelper(Context context) {
-        mContext = context;
-        mAM = ActivityManagerWrapper.getInstance();
-        mMainThreadExecutor = new MainThreadExecutor();
-        mRecentsModel = RecentsModel.getInstance(mContext);
-
-        Intent myHomeIntent = new Intent(Intent.ACTION_MAIN)
-                .addCategory(Intent.CATEGORY_HOME)
-                .setPackage(mContext.getPackageName());
-        ResolveInfo info = context.getPackageManager().resolveActivity(myHomeIntent, 0);
-        mMyHomeComponent = new ComponentName(context.getPackageName(), info.activityInfo.name);
-
-        mContext.registerReceiver(mUserPreferenceChangeReceiver,
-                new IntentFilter(ACTION_PREFERRED_ACTIVITY_CHANGED));
-        initOverviewTargets();
-    }
-
-    private void initOverviewTargets() {
-        ComponentName defaultHome = PackageManagerWrapper.getInstance()
-                .getHomeActivities(new ArrayList<>());
-
-        final String overviewIntentCategory;
-        if (defaultHome == null || mMyHomeComponent.equals(defaultHome)) {
-            // User default home is same as out home app. Use Overview integrated in Launcher.
-            overviewComponent = mMyHomeComponent;
-            mActivityControlHelper = new LauncherActivityControllerHelper();
-            overviewIntentCategory = Intent.CATEGORY_HOME;
-
-            if (mUpdateRegisteredPackage != null) {
-                // Remove any update listener as we don't care about other packages.
-                mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
-                mUpdateRegisteredPackage = null;
-            }
-        } else {
-            // The default home app is a different launcher. Use the fallback Overview instead.
-            overviewComponent = new ComponentName(mContext, RecentsActivity.class);
-            mActivityControlHelper = new FallbackActivityControllerHelper(defaultHome);
-            overviewIntentCategory = Intent.CATEGORY_DEFAULT;
-
-            // User's default home app can change as a result of package updates of this app (such
-            // as uninstalling the app or removing the "Launcher" feature in an update).
-            // Listen for package updates of this app (and remove any previously attached
-            // package listener).
-            if (!defaultHome.getPackageName().equals(mUpdateRegisteredPackage)) {
-                if (mUpdateRegisteredPackage != null) {
-                    mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
-                }
-
-                mUpdateRegisteredPackage = defaultHome.getPackageName();
-                IntentFilter updateReceiver = new IntentFilter(ACTION_PACKAGE_ADDED);
-                updateReceiver.addAction(ACTION_PACKAGE_CHANGED);
-                updateReceiver.addAction(ACTION_PACKAGE_REMOVED);
-                updateReceiver.addDataScheme("package");
-                updateReceiver.addDataSchemeSpecificPart(mUpdateRegisteredPackage,
-                        PatternMatcher.PATTERN_LITERAL);
-                mContext.registerReceiver(mOtherHomeAppUpdateReceiver, updateReceiver);
-            }
-        }
-
-        overviewIntent = new Intent(Intent.ACTION_MAIN)
-                .addCategory(overviewIntentCategory)
-                .setComponent(overviewComponent)
-                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-    }
-
-    public void onDestroy() {
-        mContext.unregisterReceiver(mUserPreferenceChangeReceiver);
-
-        if (mUpdateRegisteredPackage != null) {
-            mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
-            mUpdateRegisteredPackage = null;
-        }
-    }
-
-    public void onOverviewToggle() {
-        // If currently screen pinning, do not enter overview
-        if (mAM.isScreenPinningActive()) {
-            return;
-        }
-
-        mAM.closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
-        mMainThreadExecutor.execute(new RecentsActivityCommand<>());
-    }
-
-    public void onOverviewShown() {
-        mMainThreadExecutor.execute(new ShowRecentsCommand());
-    }
-
-    public void onTip(int actionType, int viewType) {
-        mMainThreadExecutor.execute(new Runnable() {
-            @Override
-            public void run() {
-                UserEventDispatcher.newInstance(mContext,
-                        new InvariantDeviceProfile(mContext).getDeviceProfile(mContext))
-                        .logActionTip(actionType, viewType);
-            }
-        });
-    }
-
-    public ActivityControlHelper getActivityControlHelper() {
-        return mActivityControlHelper;
-    }
-
-    private class ShowRecentsCommand extends RecentsActivityCommand {
-
-        @Override
-        protected boolean handleCommand(long elapsedTime) {
-            return mHelper.getVisibleRecentsView() != null;
-        }
-    }
-
-    private class RecentsActivityCommand<T extends BaseDraggingActivity> implements Runnable {
-
-        protected final ActivityControlHelper<T> mHelper;
-        private final long mCreateTime;
-        private final int mRunningTaskId;
-
-        private ActivityInitListener mListener;
-        private T mActivity;
-        private RecentsView mRecentsView;
-        private final long mToggleClickedTime = SystemClock.uptimeMillis();
-        private boolean mUserEventLogged;
-
-        public RecentsActivityCommand() {
-            mHelper = getActivityControlHelper();
-            mCreateTime = SystemClock.elapsedRealtime();
-            mRunningTaskId = mAM.getRunningTask().id;
-
-            // Preload the plan
-            mRecentsModel.loadTasks(mRunningTaskId, null);
-        }
-
-        @Override
-        public void run() {
-            long elapsedTime = mCreateTime - mLastToggleTime;
-            mLastToggleTime = mCreateTime;
-
-            if (!handleCommand(elapsedTime)) {
-                // Start overview
-                if (!mHelper.switchToRecentsIfVisible(true)) {
-                    mListener = mHelper.createActivityInitListener(this::onActivityReady);
-                    mListener.registerAndStartActivity(overviewIntent, this::createWindowAnimation,
-                            mContext, mMainThreadExecutor.getHandler(), RECENTS_LAUNCH_DURATION);
-                }
-            }
-        }
-
-        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 = mHelper.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(T activity, Boolean wasVisible) {
-            activity.<RecentsView>getOverviewPanel().setCurrentTask(mRunningTaskId);
-            AbstractFloatingView.closeAllOpenViews(activity, wasVisible);
-            AnimationFactory factory = mHelper.prepareRecentsUI(activity, wasVisible,
-                    (controller) -> {
-                        controller.dispatchOnStart();
-                        ValueAnimator anim = controller.getAnimationPlayer()
-                                .setDuration(RECENTS_LAUNCH_DURATION);
-                        anim.setInterpolator(FAST_OUT_SLOW_IN);
-                        anim.start();
-                });
-            factory.onRemoteAnimationReceived(null);
-            if (wasVisible) {
-                factory.createActivityController(RECENTS_LAUNCH_DURATION, INTERACTION_NORMAL);
-            }
-            mActivity = activity;
-            mRecentsView = mActivity.getOverviewPanel();
-            mRecentsView.setRunningTaskIconScaledDown(true /* isScaledDown */, false /* animate */);
-            if (!mUserEventLogged) {
-                activity.getUserEventDispatcher().logActionCommand(Action.Command.RECENTS_BUTTON,
-                        mHelper.getContainerType(), ContainerType.TASKSWITCHER);
-                mUserEventLogged = true;
-            }
-            return false;
-        }
-
-        private AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] targetCompats) {
-            if (LatencyTrackerCompat.isEnabled(mContext)) {
-                LatencyTrackerCompat.logToggleRecents(
-                        (int) (SystemClock.uptimeMillis() - mToggleClickedTime));
-            }
-
-            if (mListener != null) {
-                mListener.unregister();
-            }
-            AnimatorSet anim = new AnimatorSet();
-            anim.addListener(new AnimationSuccessListener() {
-                @Override
-                public void onAnimationSuccess(Animator animator) {
-                    if (mRecentsView != null) {
-                        mRecentsView.setRunningTaskIconScaledDown(false /* isScaledDown */,
-                                true /* animate */);
-                    }
-                }
-            });
-            if (mActivity == null) {
-                Log.e(TAG, "Animation created, before activity");
-                anim.play(ValueAnimator.ofInt(0, 1).setDuration(100));
-                return anim;
-            }
-
-            RemoteAnimationTargetSet targetSet =
-                    new RemoteAnimationTargetSet(targetCompats, MODE_CLOSING);
-
-            // Use the top closing app to determine the insets for the animation
-            RemoteAnimationTargetCompat runningTaskTarget = targetSet.findTask(mRunningTaskId);
-            if (runningTaskTarget == null) {
-                Log.e(TAG, "No closing app");
-                anim.play(ValueAnimator.ofInt(0, 1).setDuration(100));
-                return anim;
-            }
-
-            final ClipAnimationHelper clipHelper = new ClipAnimationHelper();
-
-            // At this point, the activity is already started and laid-out. Get the home-bounds
-            // relative to the screen using the rootView of the activity.
-            int loc[] = new int[2];
-            View rootView = mActivity.getRootView();
-            rootView.getLocationOnScreen(loc);
-            Rect homeBounds = new Rect(loc[0], loc[1],
-                    loc[0] + rootView.getWidth(), loc[1] + rootView.getHeight());
-            clipHelper.updateSource(homeBounds, runningTaskTarget);
-
-            TransformedRect targetRect = new TransformedRect();
-            mHelper.getSwipeUpDestinationAndLength(mActivity.getDeviceProfile(), mActivity,
-                    INTERACTION_NORMAL, targetRect);
-            clipHelper.updateTargetRect(targetRect);
-            clipHelper.prepareAnimation(false /* isOpening */);
-
-            SyncRtSurfaceTransactionApplier syncTransactionApplier =
-                    new SyncRtSurfaceTransactionApplier(rootView);
-            ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
-            valueAnimator.setDuration(RECENTS_LAUNCH_DURATION);
-            valueAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
-            valueAnimator.addUpdateListener((v) ->
-                    clipHelper.applyTransform(targetSet, (float) v.getAnimatedValue(),
-                            syncTransactionApplier));
-
-            if (targetSet.isAnimatingHome()) {
-                // If we are animating home, fade in the opening targets
-                RemoteAnimationTargetSet openingSet =
-                        new RemoteAnimationTargetSet(targetCompats, MODE_OPENING);
-
-                TransactionCompat transaction = new TransactionCompat();
-                valueAnimator.addUpdateListener((v) -> {
-                    for (RemoteAnimationTargetCompat app : openingSet.apps) {
-                        transaction.setAlpha(app.leash, (float) v.getAnimatedValue());
-                    }
-                    transaction.apply();
-                });
-            }
-            anim.play(valueAnimator);
-            return anim;
-        }
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
new file mode 100644
index 0000000..0738aff
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -0,0 +1,194 @@
+/*
+ * 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.content.Intent.ACTION_PACKAGE_ADDED;
+import static android.content.Intent.ACTION_PACKAGE_CHANGED;
+import static android.content.Intent.ACTION_PACKAGE_REMOVED;
+
+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_HOME_DISABLED;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ResolveInfo;
+
+import com.android.systemui.shared.system.PackageManagerWrapper;
+
+import java.util.ArrayList;
+
+/**
+ * Class to keep track of the current overview component based off user preferences and app updates
+ * and provide callers the relevant classes.
+ */
+public final class OverviewComponentObserver {
+    private final BroadcastReceiver mUserPreferenceChangeReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            updateOverviewTargets();
+        }
+    };
+    private final BroadcastReceiver mOtherHomeAppUpdateReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            updateOverviewTargets();
+        }
+    };
+    private final Context mContext;
+    private final ComponentName mMyHomeComponent;
+    private String mUpdateRegisteredPackage;
+    private ActivityControlHelper mActivityControlHelper;
+    private Intent mOverviewIntent;
+    private Intent mHomeIntent;
+    private int mSystemUiStateFlags;
+    private boolean mIsHomeAndOverviewSame;
+
+    public OverviewComponentObserver(Context context) {
+        mContext = context;
+
+        Intent myHomeIntent = new Intent(Intent.ACTION_MAIN)
+                .addCategory(Intent.CATEGORY_HOME)
+                .setPackage(mContext.getPackageName());
+        ResolveInfo info = context.getPackageManager().resolveActivity(myHomeIntent, 0);
+        mMyHomeComponent = new ComponentName(context.getPackageName(), info.activityInfo.name);
+
+        mContext.registerReceiver(mUserPreferenceChangeReceiver,
+                new IntentFilter(ACTION_PREFERRED_ACTIVITY_CHANGED));
+        updateOverviewTargets();
+    }
+
+    public void onSystemUiStateChanged(int stateFlags) {
+        boolean homeDisabledChanged = (mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED)
+                != (stateFlags & SYSUI_STATE_HOME_DISABLED);
+        mSystemUiStateFlags = stateFlags;
+        if (homeDisabledChanged) {
+            updateOverviewTargets();
+        }
+    }
+
+    /**
+     * Update overview intent and {@link ActivityControlHelper} based off the current launcher home
+     * component.
+     */
+    private void updateOverviewTargets() {
+        ComponentName defaultHome = PackageManagerWrapper.getInstance()
+                .getHomeActivities(new ArrayList<>());
+
+        final String overviewIntentCategory;
+        ComponentName overviewComponent;
+        mHomeIntent = null;
+
+        if ((mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) == 0 &&
+                (defaultHome == null || mMyHomeComponent.equals(defaultHome))) {
+            // User default home is same as out home app. Use Overview integrated in Launcher.
+            overviewComponent = mMyHomeComponent;
+            mActivityControlHelper = new LauncherActivityControllerHelper();
+            mIsHomeAndOverviewSame = true;
+            overviewIntentCategory = Intent.CATEGORY_HOME;
+
+            if (mUpdateRegisteredPackage != null) {
+                // Remove any update listener as we don't care about other packages.
+                mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
+                mUpdateRegisteredPackage = null;
+            }
+        } else {
+            // The default home app is a different launcher. Use the fallback Overview instead.
+            overviewComponent = new ComponentName(mContext, RecentsActivity.class);
+            mActivityControlHelper = new FallbackActivityControllerHelper();
+            mIsHomeAndOverviewSame = false;
+            overviewIntentCategory = Intent.CATEGORY_DEFAULT;
+
+            mHomeIntent = new Intent(Intent.ACTION_MAIN)
+                    .addCategory(Intent.CATEGORY_HOME)
+                    .setComponent(defaultHome);
+            // User's default home app can change as a result of package updates of this app (such
+            // as uninstalling the app or removing the "Launcher" feature in an update).
+            // Listen for package updates of this app (and remove any previously attached
+            // package listener).
+            if (defaultHome == null) {
+                if (mUpdateRegisteredPackage != null) {
+                    mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
+                }
+            } else if (!defaultHome.getPackageName().equals(mUpdateRegisteredPackage)) {
+                if (mUpdateRegisteredPackage != null) {
+                    mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
+                }
+
+                mUpdateRegisteredPackage = defaultHome.getPackageName();
+                mContext.registerReceiver(mOtherHomeAppUpdateReceiver, getPackageFilter(
+                        mUpdateRegisteredPackage, ACTION_PACKAGE_ADDED, ACTION_PACKAGE_CHANGED,
+                        ACTION_PACKAGE_REMOVED));
+            }
+        }
+
+        mOverviewIntent = new Intent(Intent.ACTION_MAIN)
+                .addCategory(overviewIntentCategory)
+                .setComponent(overviewComponent)
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        if (mHomeIntent == null) {
+            mHomeIntent = mOverviewIntent;
+        }
+    }
+
+    /**
+     * Clean up any registered receivers.
+     */
+    public void onDestroy() {
+        mContext.unregisterReceiver(mUserPreferenceChangeReceiver);
+
+        if (mUpdateRegisteredPackage != null) {
+            mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
+            mUpdateRegisteredPackage = null;
+        }
+    }
+
+    /**
+     * Get the current intent for going to the overview activity.
+     *
+     * @return the overview intent
+     */
+    public Intent getOverviewIntent() {
+        return mOverviewIntent;
+    }
+
+    /**
+     * Get the current intent for going to the home activity.
+     */
+    public Intent getHomeIntent() {
+        return mHomeIntent;
+    }
+
+    /**
+     * Returns true if home and overview are same activity.
+     */
+    public boolean isHomeAndOverviewSame() {
+        return mIsHomeAndOverviewSame;
+    }
+
+    /**
+     * Get the current activity control helper for managing interactions to the overview activity.
+     *
+     * @return the current activity control helper
+     */
+    public ActivityControlHelper getActivityControlHelper() {
+        return mActivityControlHelper;
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/OverviewInteractionState.java b/quickstep/src/com/android/quickstep/OverviewInteractionState.java
index 922a7ff..78b48d7 100644
--- a/quickstep/src/com/android/quickstep/OverviewInteractionState.java
+++ b/quickstep/src/com/android/quickstep/OverviewInteractionState.java
@@ -15,74 +15,35 @@
  */
 package com.android.quickstep;
 
-import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_QUICK_SCRUB;
-import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_SWIPE_UP;
-import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON;
-import static com.android.systemui.shared.system.SettingsCompat.SWIPE_UP_SETTING_NAME;
-
-import android.content.ContentResolver;
 import android.content.Context;
-import android.content.res.Resources;
-import android.database.ContentObserver;
 import android.os.Handler;
-import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
-import android.provider.Settings;
-import android.support.annotation.WorkerThread;
 import android.util.Log;
 
-import com.android.launcher3.MainThreadExecutor;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.allapps.DiscoveryBounce;
+import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.UiThreadHelper;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 
-import java.util.concurrent.ExecutionException;
+import androidx.annotation.WorkerThread;
 
 /**
- * Sets overview interaction flags, such as:
- *
- *   - FLAG_DISABLE_QUICK_SCRUB
- *   - FLAG_DISABLE_SWIPE_UP
- *   - FLAG_SHOW_OVERVIEW_BUTTON
- *
- * @see com.android.systemui.shared.system.NavigationBarCompat.InteractionType and associated flags.
+ * Sets alpha for the back button
  */
 public class OverviewInteractionState {
 
     private static final String TAG = "OverviewFlags";
 
     private static final String HAS_ENABLED_QUICKSTEP_ONCE = "launcher.has_enabled_quickstep_once";
-    private static final String SWIPE_UP_SETTING_AVAILABLE_RES_NAME =
-            "config_swipe_up_gesture_setting_available";
-    private static final String SWIPE_UP_ENABLED_DEFAULT_RES_NAME =
-            "config_swipe_up_gesture_default";
 
     // We do not need any synchronization for this variable as its only written on UI thread.
-    private static OverviewInteractionState INSTANCE;
-
-    public static OverviewInteractionState getInstance(final Context context) {
-        if (INSTANCE == null) {
-            if (Looper.myLooper() == Looper.getMainLooper()) {
-                INSTANCE = new OverviewInteractionState(context.getApplicationContext());
-            } else {
-                try {
-                    return new MainThreadExecutor().submit(
-                            () -> OverviewInteractionState.getInstance(context)).get();
-                } catch (InterruptedException|ExecutionException e) {
-                    throw new RuntimeException(e);
-                }
-            }
-        }
-        return INSTANCE;
-    }
+    public static final MainThreadInitializedObject<OverviewInteractionState> INSTANCE =
+            new MainThreadInitializedObject<>(OverviewInteractionState::new);
 
     private static final int MSG_SET_PROXY = 200;
     private static final int MSG_SET_BACK_BUTTON_ALPHA = 201;
-    private static final int MSG_SET_SWIPE_UP_ENABLED = 202;
-
-    private final SwipeUpGestureEnabledSettingObserver mSwipeUpSettingObserver;
 
     private final Context mContext;
     private final Handler mUiHandler;
@@ -90,10 +51,9 @@
 
     // These are updated on the background thread
     private ISystemUiProxy mISystemUiProxy;
-    private boolean mSwipeUpEnabled = true;
     private float mBackButtonAlpha = 1;
 
-    private Runnable mOnSwipeUpSettingChangedListener;
+    private int mSystemUiStateFlags;
 
     private OverviewInteractionState(Context context) {
         mContext = context;
@@ -104,18 +64,8 @@
         mUiHandler = new Handler(this::handleUiMessage);
         mBgHandler = new Handler(UiThreadHelper.getBackgroundLooper(), this::handleBgMessage);
 
-        if (getSystemBooleanRes(SWIPE_UP_SETTING_AVAILABLE_RES_NAME)) {
-            mSwipeUpSettingObserver = new SwipeUpGestureEnabledSettingObserver(mUiHandler,
-                    context.getContentResolver());
-            mSwipeUpSettingObserver.register();
-        } else {
-            mSwipeUpSettingObserver = null;
-            mSwipeUpEnabled = getSystemBooleanRes(SWIPE_UP_ENABLED_DEFAULT_RES_NAME);
-        }
-    }
-
-    public boolean isSwipeUpGestureEnabled() {
-        return mSwipeUpEnabled;
+        onNavigationModeChanged(SysUINavigationMode.INSTANCE.get(context)
+                .addModeChangeListener(this::onNavigationModeChanged));
     }
 
     public float getBackButtonAlpha() {
@@ -123,7 +73,7 @@
     }
 
     public void setBackButtonAlpha(float alpha, boolean animate) {
-        if (!mSwipeUpEnabled) {
+        if (!modeSupportsGestures()) {
             alpha = 1;
         }
         mUiHandler.removeMessages(MSG_SET_BACK_BUTTON_ALPHA);
@@ -135,6 +85,14 @@
         mBgHandler.obtainMessage(MSG_SET_PROXY, proxy).sendToTarget();
     }
 
+    public void setSystemUiStateFlags(int stateFlags) {
+        mSystemUiStateFlags = stateFlags;
+    }
+
+    public int getSystemUiStateFlags() {
+        return mSystemUiStateFlags;
+    }
+
     private boolean handleUiMessage(Message msg) {
         if (msg.what == MSG_SET_BACK_BUTTON_ALPHA) {
             mBackButtonAlpha = (float) msg.obj;
@@ -151,40 +109,10 @@
             case MSG_SET_BACK_BUTTON_ALPHA:
                 applyBackButtonAlpha((float) msg.obj, msg.arg1 == 1);
                 return true;
-            case MSG_SET_SWIPE_UP_ENABLED:
-                mSwipeUpEnabled = msg.arg1 != 0;
-                resetHomeBounceSeenOnQuickstepEnabledFirstTime();
-
-                if (mOnSwipeUpSettingChangedListener != null) {
-                    mOnSwipeUpSettingChangedListener.run();
-                }
-                break;
         }
-        applyFlags();
         return true;
     }
 
-    public void setOnSwipeUpSettingChangedListener(Runnable listener) {
-        mOnSwipeUpSettingChangedListener = listener;
-    }
-
-    @WorkerThread
-    private void applyFlags() {
-        if (mISystemUiProxy == null) {
-            return;
-        }
-
-        int flags = 0;
-        if (!mSwipeUpEnabled) {
-            flags = FLAG_DISABLE_SWIPE_UP | FLAG_DISABLE_QUICK_SCRUB | FLAG_SHOW_OVERVIEW_BUTTON;
-        }
-        try {
-            mISystemUiProxy.setInteractionState(flags);
-        } catch (RemoteException e) {
-            Log.w(TAG, "Unable to update overview interaction flags", e);
-        }
-    }
-
     @WorkerThread
     private void applyBackButtonAlpha(float alpha, boolean animate) {
         if (mISystemUiProxy == null) {
@@ -197,56 +125,21 @@
         }
     }
 
-    private class SwipeUpGestureEnabledSettingObserver extends ContentObserver {
-        private Handler mHandler;
-        private ContentResolver mResolver;
-        private final int defaultValue;
-
-        SwipeUpGestureEnabledSettingObserver(Handler handler, ContentResolver resolver) {
-            super(handler);
-            mHandler = handler;
-            mResolver = resolver;
-            defaultValue = getSystemBooleanRes(SWIPE_UP_ENABLED_DEFAULT_RES_NAME) ? 1 : 0;
-        }
-
-        public void register() {
-            mResolver.registerContentObserver(Settings.Secure.getUriFor(SWIPE_UP_SETTING_NAME),
-                    false, this);
-            mSwipeUpEnabled = getValue();
-            resetHomeBounceSeenOnQuickstepEnabledFirstTime();
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            super.onChange(selfChange);
-            mHandler.removeMessages(MSG_SET_SWIPE_UP_ENABLED);
-            mHandler.obtainMessage(MSG_SET_SWIPE_UP_ENABLED, getValue() ? 1 : 0, 0).sendToTarget();
-        }
-
-        private boolean getValue() {
-            return Settings.Secure.getInt(mResolver, SWIPE_UP_SETTING_NAME, defaultValue) == 1;
-        }
-    }
-
-    private boolean getSystemBooleanRes(String resName) {
-        Resources res = Resources.getSystem();
-        int resId = res.getIdentifier(resName, "bool", "android");
-
-        if (resId != 0) {
-            return res.getBoolean(resId);
-        } else {
-            Log.e(TAG, "Failed to get system resource ID. Incompatible framework version?");
-            return false;
-        }
+    private void onNavigationModeChanged(SysUINavigationMode.Mode mode) {
+        resetHomeBounceSeenOnQuickstepEnabledFirstTime();
     }
 
     private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() {
-        if (mSwipeUpEnabled && !Utilities.getPrefs(mContext).getBoolean(
+        if (modeSupportsGestures() && !Utilities.getPrefs(mContext).getBoolean(
                 HAS_ENABLED_QUICKSTEP_ONCE, true)) {
             Utilities.getPrefs(mContext).edit()
-                    .putBoolean(HAS_ENABLED_QUICKSTEP_ONCE, true)
-                    .putBoolean(DiscoveryBounce.HOME_BOUNCE_SEEN, false)
-                    .apply();
+                .putBoolean(HAS_ENABLED_QUICKSTEP_ONCE, true)
+                .putBoolean(DiscoveryBounce.HOME_BOUNCE_SEEN, false)
+                .apply();
         }
     }
+
+    private boolean modeSupportsGestures() {
+        return SysUINavigationMode.getMode(mContext).hasGestures;
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/QuickScrubController.java b/quickstep/src/com/android/quickstep/QuickScrubController.java
deleted file mode 100644
index cbc7a67..0000000
--- a/quickstep/src/com/android/quickstep/QuickScrubController.java
+++ /dev/null
@@ -1,258 +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.anim.Interpolators.FAST_OUT_SLOW_IN;
-
-import android.util.Log;
-import android.view.HapticFeedbackConstants;
-import android.view.animation.Interpolator;
-
-import com.android.launcher3.Alarm;
-import com.android.launcher3.BaseActivity;
-import com.android.launcher3.OnAlarmListener;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskView;
-
-/**
- * Responds to quick scrub callbacks to page through and launch recent tasks.
- *
- * The behavior is to evenly divide the progress into sections, each of which scrolls one page.
- * The first and last section set an alarm to auto-advance backwards or forwards, respectively.
- */
-public class QuickScrubController implements OnAlarmListener {
-
-    public static final int QUICK_SCRUB_FROM_APP_START_DURATION = 240;
-    public static final int QUICK_SCRUB_FROM_HOME_START_DURATION = 200;
-    // We want the translation y to finish faster than the rest of the animation.
-    public static final float QUICK_SCRUB_TRANSLATION_Y_FACTOR = 5f / 6;
-    public static final Interpolator QUICK_SCRUB_START_INTERPOLATOR = FAST_OUT_SLOW_IN;
-
-    /**
-     * Snap to a new page when crossing these thresholds. The first and last auto-advance.
-     */
-    private static final float[] QUICK_SCRUB_THRESHOLDS = new float[] {
-            0.05f, 0.20f, 0.35f, 0.50f, 0.65f, 0.80f, 0.95f
-    };
-
-    private static final String TAG = "QuickScrubController";
-    private static final boolean ENABLE_AUTO_ADVANCE = true;
-    private static final long AUTO_ADVANCE_DELAY = 500;
-    private static final int QUICKSCRUB_SNAP_DURATION_PER_PAGE = 325;
-    private static final int QUICKSCRUB_END_SNAP_DURATION_PER_PAGE = 60;
-
-    private final Alarm mAutoAdvanceAlarm;
-    private final RecentsView mRecentsView;
-    private final BaseActivity mActivity;
-
-    private boolean mInQuickScrub;
-    private boolean mWaitingForTaskLaunch;
-    private int mQuickScrubSection;
-    private boolean mStartedFromHome;
-    private boolean mFinishedTransitionToQuickScrub;
-    private Runnable mOnFinishedTransitionToQuickScrubRunnable;
-    private ActivityControlHelper mActivityControlHelper;
-
-    public QuickScrubController(BaseActivity activity, RecentsView recentsView) {
-        mActivity = activity;
-        mRecentsView = recentsView;
-        if (ENABLE_AUTO_ADVANCE) {
-            mAutoAdvanceAlarm = new Alarm();
-            mAutoAdvanceAlarm.setOnAlarmListener(this);
-        }
-    }
-
-    public void onQuickScrubStart(boolean startingFromHome, ActivityControlHelper controlHelper) {
-        prepareQuickScrub(TAG);
-        mInQuickScrub = true;
-        mStartedFromHome = startingFromHome;
-        mQuickScrubSection = 0;
-        mFinishedTransitionToQuickScrub = false;
-        mActivityControlHelper = controlHelper;
-
-        snapToNextTaskIfAvailable();
-        mActivity.getUserEventDispatcher().resetActionDurationMillis();
-    }
-
-    public void onQuickScrubEnd() {
-        mInQuickScrub = false;
-        if (ENABLE_AUTO_ADVANCE) {
-            mAutoAdvanceAlarm.cancelAlarm();
-        }
-        int page = mRecentsView.getNextPage();
-        Runnable launchTaskRunnable = () -> {
-            TaskView taskView = mRecentsView.getTaskViewAt(page);
-            if (taskView != null) {
-                mWaitingForTaskLaunch = true;
-                taskView.launchTask(true, (result) -> {
-                    if (!result) {
-                        taskView.notifyTaskLaunchFailed(TAG);
-                        breakOutOfQuickScrub();
-                    } else {
-                        mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(Touch.DRAGDROP,
-                                LauncherLogProto.Action.Direction.NONE, page,
-                                TaskUtils.getLaunchComponentKeyForTask(taskView.getTask().key));
-                    }
-                    mWaitingForTaskLaunch = false;
-                }, taskView.getHandler());
-            } else {
-                breakOutOfQuickScrub();
-            }
-            mActivityControlHelper = null;
-        };
-        int snapDuration = Math.abs(page - mRecentsView.getPageNearestToCenterOfScreen())
-                * QUICKSCRUB_END_SNAP_DURATION_PER_PAGE;
-        if (mRecentsView.getChildCount() > 0 && mRecentsView.snapToPage(page, snapDuration)) {
-            // Settle on the page then launch it
-            mRecentsView.setNextPageSwitchRunnable(launchTaskRunnable);
-        } else {
-            // No page move needed, just launch it
-            if (mFinishedTransitionToQuickScrub) {
-                launchTaskRunnable.run();
-            } else {
-                mOnFinishedTransitionToQuickScrubRunnable = launchTaskRunnable;
-            }
-        }
-    }
-
-    public void cancelActiveQuickscrub() {
-        if (!mInQuickScrub) {
-            return;
-        }
-        Log.d(TAG, "Quickscrub was active, cancelling");
-        mInQuickScrub = false;
-        mActivityControlHelper = null;
-        mOnFinishedTransitionToQuickScrubRunnable = null;
-        mRecentsView.setNextPageSwitchRunnable(null);
-    }
-
-    /**
-     * Initializes the UI for quick scrub, returns true if success.
-     */
-    public boolean prepareQuickScrub(String tag) {
-        if (mWaitingForTaskLaunch || mInQuickScrub) {
-            Log.d(tag, "Waiting for last scrub to finish, will skip this interaction");
-            return false;
-        }
-        mOnFinishedTransitionToQuickScrubRunnable = null;
-        mRecentsView.setNextPageSwitchRunnable(null);
-        return true;
-    }
-
-    public boolean isWaitingForTaskLaunch() {
-        return mWaitingForTaskLaunch;
-    }
-
-    /**
-     * Attempts to go to normal overview or back to home, so UI doesn't prevent user interaction.
-     */
-    private void breakOutOfQuickScrub() {
-        if (mRecentsView.getChildCount() == 0 || mActivityControlHelper == null
-                || !mActivityControlHelper.switchToRecentsIfVisible(false)) {
-            mActivity.onBackPressed();
-        }
-    }
-
-    public void onQuickScrubProgress(float progress) {
-        int quickScrubSection = 0;
-        for (float threshold : QUICK_SCRUB_THRESHOLDS) {
-            if (progress < threshold) {
-                break;
-            }
-            quickScrubSection++;
-        }
-        if (quickScrubSection != mQuickScrubSection) {
-            boolean cameFromAutoAdvance = mQuickScrubSection == QUICK_SCRUB_THRESHOLDS.length
-                    || mQuickScrubSection == 0;
-            int pageToGoTo = mRecentsView.getNextPage() + quickScrubSection - mQuickScrubSection;
-            if (mFinishedTransitionToQuickScrub && !cameFromAutoAdvance) {
-                goToPageWithHaptic(pageToGoTo);
-            }
-            if (ENABLE_AUTO_ADVANCE) {
-                if (quickScrubSection == QUICK_SCRUB_THRESHOLDS.length || quickScrubSection == 0) {
-                    mAutoAdvanceAlarm.setAlarm(AUTO_ADVANCE_DELAY);
-                } else {
-                    mAutoAdvanceAlarm.cancelAlarm();
-                }
-            }
-            mQuickScrubSection = quickScrubSection;
-        }
-    }
-
-    public void onFinishedTransitionToQuickScrub() {
-        mFinishedTransitionToQuickScrub = true;
-        Runnable action = mOnFinishedTransitionToQuickScrubRunnable;
-        // Clear the runnable before executing it, to prevent potential recursion.
-        mOnFinishedTransitionToQuickScrubRunnable = null;
-        if (action != null) {
-            action.run();
-        }
-    }
-
-    public void snapToNextTaskIfAvailable() {
-        if (mInQuickScrub && mRecentsView.getChildCount() > 0) {
-            int duration = mStartedFromHome ? QUICK_SCRUB_FROM_HOME_START_DURATION
-                    : QUICK_SCRUB_FROM_APP_START_DURATION;
-            int pageToGoTo = mStartedFromHome ? 0 : mRecentsView.getNextPage() + 1;
-            goToPageWithHaptic(pageToGoTo, duration, true /* forceHaptic */,
-                    QUICK_SCRUB_START_INTERPOLATOR);
-        }
-    }
-
-    private void goToPageWithHaptic(int pageToGoTo) {
-        goToPageWithHaptic(pageToGoTo, -1 /* overrideDuration */, false /* forceHaptic */, null);
-    }
-
-    private void goToPageWithHaptic(int pageToGoTo, int overrideDuration, boolean forceHaptic,
-            Interpolator interpolator) {
-        pageToGoTo = Utilities.boundToRange(pageToGoTo, 0, mRecentsView.getTaskViewCount() - 1);
-        boolean snappingToPage = pageToGoTo != mRecentsView.getNextPage();
-        if (snappingToPage) {
-            int duration = overrideDuration > -1 ? overrideDuration
-                    : Math.abs(pageToGoTo - mRecentsView.getNextPage())
-                            * QUICKSCRUB_SNAP_DURATION_PER_PAGE;
-            mRecentsView.snapToPage(pageToGoTo, duration, interpolator);
-        }
-        if (snappingToPage || forceHaptic) {
-            mRecentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
-                    HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
-        }
-    }
-
-    @Override
-    public void onAlarm(Alarm alarm) {
-        int currPage = mRecentsView.getNextPage();
-        boolean recentsVisible = mActivityControlHelper != null
-                && mActivityControlHelper.getVisibleRecentsView() != null;
-        if (!recentsVisible) {
-            Log.w(TAG, "Failed to auto advance; recents not visible");
-            return;
-        }
-        if (mQuickScrubSection == QUICK_SCRUB_THRESHOLDS.length
-                && currPage < mRecentsView.getTaskViewCount() - 1) {
-            goToPageWithHaptic(currPage + 1);
-        } else if (mQuickScrubSection == 0 && currPage > 0) {
-            goToPageWithHaptic(currPage - 1);
-        }
-        if (ENABLE_AUTO_ADVANCE) {
-            mAutoAdvanceAlarm.setAlarm(AUTO_ADVANCE_DELAY);
-        }
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
index 2c3f77f..7bfa9a0 100644
--- a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
+++ b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
@@ -15,18 +15,50 @@
  */
 package com.android.quickstep;
 
+import android.app.ActivityManager;
 import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.UserManager;
+import android.util.Log;
 
+import com.android.launcher3.BuildConfig;
 import com.android.launcher3.MainProcessInitializer;
+import com.android.launcher3.Utilities;
 import com.android.systemui.shared.system.ThreadedRendererCompat;
 
 @SuppressWarnings("unused")
 public class QuickstepProcessInitializer extends MainProcessInitializer {
 
+    private static final String TAG = "QuickstepProcessInitializer";
+    private static final int HEAP_LIMIT_MB = 250;
+
     public QuickstepProcessInitializer(Context context) { }
 
     @Override
     protected void init(Context context) {
+        if (Utilities.IS_DEBUG_DEVICE) {
+            try {
+                // Trigger a heap dump if the PSS reaches beyond the target heap limit
+                final ActivityManager am = context.getSystemService(ActivityManager.class);
+                am.setWatchHeapLimit(HEAP_LIMIT_MB * 1024 * 1024);
+            } catch (SecurityException e) {
+                // Do nothing
+            }
+        }
+
+        // Workaround for b/120550382, an external app can cause the launcher process to start for
+        // a work profile user which we do not support. Disable the application immediately when we
+        // detect this to be the case.
+        UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
+        if (um.isManagedProfile()) {
+            PackageManager pm = context.getPackageManager();
+            pm.setApplicationEnabledSetting(context.getPackageName(),
+                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 0 /* flags */);
+            Log.w(TAG, "Disabling " + BuildConfig.APPLICATION_ID
+                    + ", unable to run in a managed profile");
+            return;
+        }
+
         super.init(context);
 
         // Elevate GPU priority for Quickstep and Remote animations.
diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
new file mode 100644
index 0000000..8951363
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -0,0 +1,36 @@
+package com.android.quickstep;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import com.android.launcher3.testing.TestInformationHandler;
+import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.uioverrides.states.OverviewState;
+import com.android.quickstep.util.LayoutUtils;
+
+public class QuickstepTestInformationHandler extends TestInformationHandler {
+
+    public QuickstepTestInformationHandler(Context context) { }
+
+    @Override
+    public Bundle call(String method) {
+        final Bundle response = new Bundle();
+        switch (method) {
+            case TestProtocol.REQUEST_HOME_TO_OVERVIEW_SWIPE_HEIGHT: {
+                final float swipeHeight =
+                        OverviewState.getDefaultSwipeHeight(mDeviceProfile);
+                response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) swipeHeight);
+                return response;
+            }
+
+            case TestProtocol.REQUEST_BACKGROUND_TO_OVERVIEW_SWIPE_HEIGHT: {
+                final float swipeHeight =
+                        LayoutUtils.getShelfTrackingDistance(mContext, mDeviceProfile);
+                response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) swipeHeight);
+                return response;
+            }
+        }
+
+        return super.call(method);
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
new file mode 100644
index 0000000..f27ba85
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.annotation.TargetApi;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.os.Build;
+import android.os.Process;
+import android.util.SparseBooleanArray;
+import com.android.launcher3.MainThreadExecutor;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.BackgroundExecutor;
+import com.android.systemui.shared.system.KeyguardManagerCompat;
+import com.android.systemui.shared.system.RecentTaskInfoCompat;
+import com.android.systemui.shared.system.TaskDescriptionCompat;
+import com.android.systemui.shared.system.TaskStackChangeListener;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * Manages the recent task list from the system, caching it as necessary.
+ */
+@TargetApi(Build.VERSION_CODES.P)
+public class RecentTasksList extends TaskStackChangeListener {
+
+    private final KeyguardManagerCompat mKeyguardManager;
+    private final MainThreadExecutor mMainThreadExecutor;
+    private final BackgroundExecutor mBgThreadExecutor;
+
+    // The list change id, increments as the task list changes in the system
+    private int mChangeId;
+    // The last change id when the list was last loaded completely, must be <= the list change id
+    private int mLastLoadedId;
+    // The last change id was loaded with keysOnly  = true
+    private boolean mLastLoadHadKeysOnly;
+
+    ArrayList<Task> mTasks = new ArrayList<>();
+
+    public RecentTasksList(Context context) {
+        mMainThreadExecutor = new MainThreadExecutor();
+        mBgThreadExecutor = BackgroundExecutor.get();
+        mKeyguardManager = new KeyguardManagerCompat(context);
+        mChangeId = 1;
+        ActivityManagerWrapper.getInstance().registerTaskStackListener(this);
+    }
+
+    /**
+     * Fetches the task keys skipping any local cache.
+     */
+    public void getTaskKeys(int numTasks, Consumer<ArrayList<Task>> callback) {
+        // Kick off task loading in the background
+        mBgThreadExecutor.submit(() -> {
+            ArrayList<Task> tasks = loadTasksInBackground(numTasks, true /* loadKeysOnly */);
+            mMainThreadExecutor.execute(() -> callback.accept(tasks));
+        });
+    }
+
+    /**
+     * Asynchronously fetches the list of recent tasks, reusing cached list if available.
+     *
+     * @param loadKeysOnly Whether to load other associated task data, or just the key
+     * @param callback The callback to receive the list of recent tasks
+     * @return The change id of the current task list
+     */
+    public synchronized int getTasks(boolean loadKeysOnly, Consumer<ArrayList<Task>> callback) {
+        final int requestLoadId = mChangeId;
+        Runnable resultCallback = callback == null
+                ? () -> { }
+                : () -> callback.accept(copyOf(mTasks));
+
+        if (mLastLoadedId == mChangeId && (!mLastLoadHadKeysOnly || loadKeysOnly)) {
+            // The list is up to date, callback with the same list
+            mMainThreadExecutor.execute(resultCallback);
+            return requestLoadId;
+        }
+
+        // Kick off task loading in the background
+        mBgThreadExecutor.submit(() -> {
+            ArrayList<Task> tasks = loadTasksInBackground(Integer.MAX_VALUE, loadKeysOnly);
+
+            mMainThreadExecutor.execute(() -> {
+                mTasks = tasks;
+                mLastLoadedId = requestLoadId;
+                mLastLoadHadKeysOnly = loadKeysOnly;
+                resultCallback.run();
+            });
+        });
+
+        return requestLoadId;
+    }
+
+    /**
+     * @return Whether the provided {@param changeId} is the latest recent tasks list id.
+     */
+    public synchronized boolean isTaskListValid(int changeId) {
+        return mChangeId == changeId;
+    }
+
+    @Override
+    public synchronized void onTaskStackChanged() {
+        mChangeId++;
+    }
+
+    @Override
+    public void onTaskRemoved(int taskId) {
+        for (int i = mTasks.size() - 1; i >= 0; i--) {
+            if (mTasks.get(i).key.id == taskId) {
+                mTasks.remove(i);
+                return;
+            }
+        }
+    }
+
+    @Override
+    public synchronized void onActivityPinned(String packageName, int userId, int taskId,
+            int stackId) {
+        mChangeId++;
+    }
+
+    @Override
+    public synchronized void onActivityUnpinned() {
+        mChangeId++;
+    }
+
+    /**
+     * Loads and creates a list of all the recent tasks.
+     */
+    private ArrayList<Task> loadTasksInBackground(int numTasks,
+            boolean loadKeysOnly) {
+        int currentUserId = Process.myUserHandle().getIdentifier();
+        ArrayList<Task> allTasks = new ArrayList<>();
+        List<ActivityManager.RecentTaskInfo> rawTasks =
+                ActivityManagerWrapper.getInstance().getRecentTasks(numTasks, currentUserId);
+        // The raw tasks are given in most-recent to least-recent order, we need to reverse it
+        Collections.reverse(rawTasks);
+
+        SparseBooleanArray tmpLockedUsers = new SparseBooleanArray() {
+            @Override
+            public boolean get(int key) {
+                if (indexOfKey(key) < 0) {
+                    // Fill the cached locked state as we fetch
+                    put(key, mKeyguardManager.isDeviceLocked(key));
+                }
+                return super.get(key);
+            }
+        };
+
+        int taskCount = rawTasks.size();
+        for (int i = 0; i < taskCount; i++) {
+            ActivityManager.RecentTaskInfo rawTask = rawTasks.get(i);
+            RecentTaskInfoCompat t = new RecentTaskInfoCompat(rawTask);
+            Task.TaskKey taskKey = new Task.TaskKey(rawTask);
+            Task task;
+            if (!loadKeysOnly) {
+                ActivityManager.TaskDescription rawTd = t.getTaskDescription();
+                TaskDescriptionCompat td = new TaskDescriptionCompat(rawTd);
+                boolean isLocked = tmpLockedUsers.get(t.getUserId());
+                task = new Task(taskKey, td.getPrimaryColor(), td.getBackgroundColor(),
+                        t.supportsSplitScreenMultiWindow(), isLocked, rawTd, t.getTopActivity());
+            } else {
+                task = new Task(taskKey);
+            }
+            allTasks.add(task);
+        }
+
+        return allTasks;
+    }
+
+    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));
+        }
+        return newTasks;
+    }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
deleted file mode 100644
index 1d7c066..0000000
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ /dev/null
@@ -1,285 +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;
-
-import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
-import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
-
-import static com.android.launcher3.LauncherAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
-import static com.android.launcher3.LauncherAppTransitionManagerImpl.STATUS_BAR_TRANSITION_DURATION;
-import static com.android.launcher3.LauncherAppTransitionManagerImpl.STATUS_BAR_TRANSITION_PRE_DELAY;
-import static com.android.quickstep.TaskUtils.getRecentsWindowAnimator;
-import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
-
-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.View;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.LauncherAnimationRunner;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.R;
-import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.badge.BadgeInfo;
-import com.android.launcher3.uioverrides.UiFactory;
-import com.android.launcher3.util.SystemUiController;
-import com.android.launcher3.util.Themes;
-import com.android.launcher3.views.BaseDragLayer;
-import com.android.quickstep.fallback.FallbackRecentsView;
-import com.android.quickstep.fallback.RecentsRootView;
-import com.android.quickstep.util.ClipAnimationHelper;
-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;
-
-/**
- * A simple activity to show the recently launched tasks
- */
-public class RecentsActivity extends BaseDraggingActivity {
-
-    private Handler mUiHandler = new Handler(Looper.getMainLooper());
-    private RecentsRootView mRecentsRootView;
-    private FallbackRecentsView mFallbackRecentsView;
-
-    private Configuration mOldConfig;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        mOldConfig = new Configuration(getResources().getConfiguration());
-        initDeviceProfile();
-
-        setContentView(R.layout.fallback_recents_activity);
-        mRecentsRootView = findViewById(R.id.drag_layer);
-        mFallbackRecentsView = findViewById(R.id.overview_panel);
-
-        mRecentsRootView.setup();
-
-        getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW,
-                Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText));
-        RecentsActivityTracker.onRecentsActivityCreate(this);
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        int diff = newConfig.diff(mOldConfig);
-        if ((diff & (CONFIG_ORIENTATION | CONFIG_SCREEN_SIZE)) != 0) {
-            onHandleConfigChanged();
-        }
-        mOldConfig.setTo(newConfig);
-        super.onConfigurationChanged(newConfig);
-    }
-
-    @Override
-    public void onMultiWindowModeChanged(boolean isInMultiWindowMode, Configuration newConfig) {
-        onHandleConfigChanged();
-        super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig);
-    }
-
-    public void onRootViewSizeChanged() {
-        if (isInMultiWindowModeCompat()) {
-            onHandleConfigChanged();
-        }
-    }
-
-    private void onHandleConfigChanged() {
-        mUserEventDispatcher = null;
-        initDeviceProfile();
-
-        AbstractFloatingView.closeOpenViews(this, true,
-                AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
-        dispatchDeviceProfileChanged();
-
-        mRecentsRootView.setup();
-        reapplyUi();
-    }
-
-    @Override
-    protected void reapplyUi() {
-        mRecentsRootView.dispatchInsets();
-    }
-
-    private void initDeviceProfile() {
-        // In case we are reusing IDP, create a copy so that we dont conflict with Launcher
-        // activity.
-        LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
-        if (isInMultiWindowModeCompat()) {
-            InvariantDeviceProfile idp = appState == null
-                    ? new InvariantDeviceProfile(this) : appState.getInvariantDeviceProfile();
-            DeviceProfile dp = idp.getDeviceProfile(this);
-            mDeviceProfile = mRecentsRootView == null ? dp.copy(this)
-                    : dp.getMultiWindowProfile(this, mRecentsRootView.getLastKnownSize());
-        } else {
-            // If we are reusing the Invariant device profile, make a copy.
-            mDeviceProfile = appState == null
-                    ? new InvariantDeviceProfile(this).getDeviceProfile(this)
-                    : appState.getInvariantDeviceProfile().getDeviceProfile(this).copy(this);
-        }
-        onDeviceProfileInitiated();
-    }
-
-    @Override
-    public BaseDragLayer getDragLayer() {
-        return mRecentsRootView;
-    }
-
-    @Override
-    public View getRootView() {
-        return mRecentsRootView;
-    }
-
-    @Override
-    public <T extends View> T getOverviewPanel() {
-        return (T) mFallbackRecentsView;
-    }
-
-    @Override
-    public BadgeInfo getBadgeInfoForItem(ItemInfo info) {
-        return null;
-    }
-
-    @Override
-    public ActivityOptions getActivityLaunchOptions(final View v) {
-        if (!(v instanceof TaskView)) {
-            return null;
-        }
-
-        final TaskView taskView = (TaskView) v;
-        RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(mUiHandler,
-                true /* startAtFrontOfQueue */) {
-
-            @Override
-            public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
-                    AnimationResult result) {
-                result.setAnimation(composeRecentsLaunchAnimator(taskView, targetCompats));
-            }
-        };
-        return ActivityOptionsCompat.makeRemoteAnimation(new RemoteAnimationAdapterCompat(
-                runner, RECENTS_LAUNCH_DURATION,
-                RECENTS_LAUNCH_DURATION - STATUS_BAR_TRANSITION_DURATION
-                        - STATUS_BAR_TRANSITION_PRE_DELAY));
-    }
-
-    /**
-     * Composes the animations for a launch from the recents list if possible.
-     */
-    private AnimatorSet composeRecentsLaunchAnimator(TaskView taskView,
-            RemoteAnimationTargetCompat[] targets) {
-        AnimatorSet target = new AnimatorSet();
-        boolean activityClosing = taskIsATargetWithMode(targets, getTaskId(), MODE_CLOSING);
-        ClipAnimationHelper helper = new ClipAnimationHelper();
-        target.play(getRecentsWindowAnimator(taskView, !activityClosing, targets, helper)
-                .setDuration(RECENTS_LAUNCH_DURATION));
-
-        // Found a visible recents task that matches the opening app, lets launch the app from there
-        if (activityClosing) {
-            Animator adjacentAnimation = mFallbackRecentsView
-                    .createAdjacentPageAnimForTaskLaunch(taskView, helper);
-            adjacentAnimation.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
-            adjacentAnimation.setDuration(RECENTS_LAUNCH_DURATION);
-            adjacentAnimation.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mFallbackRecentsView.resetTaskVisuals();
-                }
-            });
-            target.play(adjacentAnimation);
-        }
-        return target;
-    }
-
-    @Override
-    public void invalidateParent(ItemInfo info) { }
-
-    @Override
-    protected void onStart() {
-        // Set the alpha to 1 before calling super, as it may get set back to 0 due to
-        // onActivityStart callback.
-        mFallbackRecentsView.setContentAlpha(1);
-        super.onStart();
-        UiFactory.onStart(this);
-        mFallbackRecentsView.resetTaskVisuals();
-    }
-
-    @Override
-    protected void onStop() {
-        super.onStop();
-
-        // Workaround for b/78520668, explicitly trim memory once UI is hidden
-        onTrimMemory(TRIM_MEMORY_UI_HIDDEN);
-    }
-
-    @Override
-    public void onEnterAnimationComplete() {
-        super.onEnterAnimationComplete();
-        UiFactory.onEnterAnimationComplete(this);
-    }
-
-    @Override
-    public void onTrimMemory(int level) {
-        super.onTrimMemory(level);
-        UiFactory.onTrimMemory(this, level);
-    }
-
-    @Override
-    protected void onNewIntent(Intent intent) {
-        super.onNewIntent(intent);
-        RecentsActivityTracker.onRecentsActivityNewIntent(this);
-    }
-
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-        RecentsActivityTracker.onRecentsActivityDestroy(this);
-    }
-
-    @Override
-    public void onBackPressed() {
-        // TODO: Launch the task we came from
-        startHome();
-    }
-
-    public void startHome() {
-        startActivity(new Intent(Intent.ACTION_MAIN)
-                .addCategory(Intent.CATEGORY_HOME)
-                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
-    }
-
-    @Override
-    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
-        super.dump(prefix, fd, writer, args);
-        writer.println(prefix + "Misc:");
-        dumpMisc(writer);
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/RecentsActivityTracker.java b/quickstep/src/com/android/quickstep/RecentsActivityTracker.java
index fb6090e..0822e61 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivityTracker.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivityTracker.java
@@ -30,17 +30,18 @@
 import java.util.function.BiPredicate;
 
 /**
- * Utility class to track create/destroy for RecentsActivity
+ * Utility class to track create/destroy for some {@link BaseRecentsActivity}.
  */
 @TargetApi(Build.VERSION_CODES.P)
-public class RecentsActivityTracker implements ActivityInitListener {
+public class RecentsActivityTracker<T extends BaseRecentsActivity> implements ActivityInitListener {
 
-    private static WeakReference<RecentsActivity> sCurrentActivity = new WeakReference<>(null);
+    private static WeakReference<BaseRecentsActivity> sCurrentActivity =
+            new WeakReference<>(null);
     private static final Scheduler sScheduler = new Scheduler();
 
-    private final BiPredicate<RecentsActivity, Boolean> mOnInitListener;
+    private final BiPredicate<T, Boolean> mOnInitListener;
 
-    public RecentsActivityTracker(BiPredicate<RecentsActivity, Boolean> onInitListener) {
+    public RecentsActivityTracker(BiPredicate<T, Boolean> onInitListener) {
         mOnInitListener = onInitListener;
     }
 
@@ -54,12 +55,12 @@
         sScheduler.clearReference(this);
     }
 
-    private boolean init(RecentsActivity activity, boolean visible) {
+    private boolean init(T activity, boolean visible) {
         return mOnInitListener.test(activity, visible);
     }
 
-    public static RecentsActivity getCurrentActivity() {
-        return sCurrentActivity.get();
+    public static <T extends BaseRecentsActivity> T getCurrentActivity() {
+        return (T) sCurrentActivity.get();
     }
 
     @Override
@@ -71,17 +72,17 @@
         context.startActivity(intent, options);
     }
 
-    public static void onRecentsActivityCreate(RecentsActivity activity) {
+    public static void onRecentsActivityCreate(BaseRecentsActivity activity) {
         sCurrentActivity = new WeakReference<>(activity);
         sScheduler.initIfPending(activity, false);
     }
 
 
-    public static void onRecentsActivityNewIntent(RecentsActivity activity) {
+    public static void onRecentsActivityNewIntent(BaseRecentsActivity activity) {
         sScheduler.initIfPending(activity, activity.isStarted());
     }
 
-    public static void onRecentsActivityDestroy(RecentsActivity activity) {
+    public static void onRecentsActivityDestroy(BaseRecentsActivity activity) {
         if (sCurrentActivity.get() == activity) {
             sCurrentActivity.clear();
         }
@@ -103,13 +104,14 @@
 
         @Override
         public void run() {
-            RecentsActivity activity = sCurrentActivity.get();
+            BaseRecentsActivity activity = sCurrentActivity.get();
             if (activity != null) {
                 initIfPending(activity, activity.isStarted());
             }
         }
 
-        public synchronized boolean initIfPending(RecentsActivity activity, boolean alreadyOnHome) {
+        public synchronized boolean initIfPending(BaseRecentsActivity activity,
+                boolean alreadyOnHome) {
             RecentsActivityTracker tracker = mPendingTracker.get();
             if (tracker != null) {
                 if (!tracker.init(activity, alreadyOnHome)) {
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java b/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java
deleted file mode 100644
index b0313fc..0000000
--- a/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java
+++ /dev/null
@@ -1,159 +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 com.android.launcher3.util.LooperExecutor;
-import com.android.launcher3.util.TraceHelper;
-import com.android.launcher3.util.UiThreadHelper;
-import com.android.quickstep.util.RemoteAnimationTargetSet;
-import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
-
-import java.util.ArrayList;
-import java.util.concurrent.ExecutorService;
-
-/**
- * Wrapper around RecentsAnimationController to help with some synchronization
- */
-public class RecentsAnimationWrapper {
-
-    // A list of callbacks to run when we receive the recents animation target. There are different
-    // than the state callbacks as these run on the current worker thread.
-    private final ArrayList<Runnable> mCallbacks = new ArrayList<>();
-
-    public RemoteAnimationTargetSet targetSet;
-
-    private RecentsAnimationControllerCompat mController;
-    private boolean mInputConsumerEnabled = false;
-    private boolean mBehindSystemBars = true;
-    private boolean mSplitScreenMinimized = false;
-
-    private final ExecutorService mExecutorService =
-            new LooperExecutor(UiThreadHelper.getBackgroundLooper());
-
-    public synchronized void setController(
-            RecentsAnimationControllerCompat controller, RemoteAnimationTargetSet targetSet) {
-        TraceHelper.partitionSection("RecentsController", "Set controller " + controller);
-        this.mController = controller;
-        this.targetSet = targetSet;
-
-        if (controller == null) {
-            return;
-        }
-        if (mInputConsumerEnabled) {
-            enableInputConsumer();
-        }
-
-        if (!mCallbacks.isEmpty()) {
-            for (Runnable action : new ArrayList<>(mCallbacks)) {
-                action.run();
-            }
-            mCallbacks.clear();
-        }
-    }
-
-    public synchronized void runOnInit(Runnable action) {
-        if (targetSet == null) {
-            mCallbacks.add(action);
-        } else {
-            action.run();
-        }
-    }
-
-    /**
-     * @param onFinishComplete A callback that runs after the animation controller has finished
-     *                         on the background thread.
-     */
-    public void finish(boolean toHome, Runnable onFinishComplete) {
-        mExecutorService.submit(() -> {
-            RecentsAnimationControllerCompat controller = mController;
-            mController = null;
-            TraceHelper.endSection("RecentsController",
-                    "Finish " + controller + ", toHome=" + toHome);
-            if (controller != null) {
-                controller.setInputConsumerEnabled(false);
-                controller.finish(toHome);
-                if (onFinishComplete != null) {
-                    onFinishComplete.run();
-                }
-            }
-        });
-    }
-
-    public void enableInputConsumer() {
-        mInputConsumerEnabled = true;
-        if (mInputConsumerEnabled) {
-            mExecutorService.submit(() -> {
-                RecentsAnimationControllerCompat controller = mController;
-                TraceHelper.partitionSection("RecentsController",
-                        "Enabling consumer on " + controller);
-                if (controller != null) {
-                    controller.setInputConsumerEnabled(true);
-                }
-            });
-        }
-    }
-
-    public void setAnimationTargetsBehindSystemBars(boolean behindSystemBars) {
-        if (mBehindSystemBars == behindSystemBars) {
-            return;
-        }
-        mBehindSystemBars = behindSystemBars;
-        mExecutorService.submit(() -> {
-            RecentsAnimationControllerCompat controller = mController;
-            TraceHelper.partitionSection("RecentsController",
-                    "Setting behind system bars on " + controller);
-            if (controller != null) {
-                controller.setAnimationTargetsBehindSystemBars(behindSystemBars);
-            }
-        });
-    }
-
-    /**
-     * NOTE: As a workaround for conflicting animations (Launcher animating the task leash, and
-     * SystemUI resizing the docked stack, which resizes the task), we currently only set the
-     * minimized mode, and not the inverse.
-     * TODO: Synchronize the minimize animation with the launcher animation
-     */
-    public void setSplitScreenMinimizedForTransaction(boolean minimized) {
-        if (mSplitScreenMinimized || !minimized) {
-            return;
-        }
-        mSplitScreenMinimized = minimized;
-        mExecutorService.submit(() -> {
-            RecentsAnimationControllerCompat controller = mController;
-            TraceHelper.partitionSection("RecentsController",
-                    "Setting minimize dock on " + controller);
-            if (controller != null) {
-                controller.setSplitScreenMinimized(minimized);
-            }
-        });
-    }
-
-    public void hideCurrentInputMethod() {
-        mExecutorService.submit(() -> {
-            RecentsAnimationControllerCompat controller = mController;
-            TraceHelper.partitionSection("RecentsController",
-                    "Hiding currentinput method on " + controller);
-            if (controller != null) {
-                controller.hideCurrentInputMethod();
-            }
-        });
-    }
-
-    public RecentsAnimationControllerCompat getController() {
-        return mController;
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 0b97f01..9f12484 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -16,41 +16,30 @@
 package com.android.quickstep;
 
 import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS;
 
 import android.annotation.TargetApi;
 import android.app.ActivityManager;
 import android.content.ComponentCallbacks2;
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.Bundle;
-import android.os.Looper;
+import android.os.HandlerThread;
+import android.os.Process;
 import android.os.RemoteException;
-import android.os.UserHandle;
-import android.support.annotation.WorkerThread;
 import android.util.Log;
-import android.util.LruCache;
-import android.util.SparseArray;
-import android.view.accessibility.AccessibilityManager;
 
-import com.android.launcher3.MainThreadExecutor;
-import com.android.launcher3.R;
-import com.android.launcher3.util.Preconditions;
+import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.systemui.shared.recents.ISystemUiProxy;
-import com.android.systemui.shared.recents.model.IconLoader;
-import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
-import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan.PreloadOptions;
-import com.android.systemui.shared.recents.model.RecentsTaskLoader;
-import com.android.systemui.shared.recents.model.TaskKeyLruCache;
+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.BackgroundExecutor;
+import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 
 import java.util.ArrayList;
-import java.util.concurrent.ExecutionException;
+import java.util.List;
 import java.util.function.Consumer;
 
 /**
@@ -58,168 +47,129 @@
  */
 @TargetApi(Build.VERSION_CODES.O)
 public class RecentsModel extends TaskStackChangeListener {
+
+    private static final String TAG = "RecentsModel";
+
     // We do not need any synchronization for this variable as its only written on UI thread.
-    private static RecentsModel INSTANCE;
+    public static final MainThreadInitializedObject<RecentsModel> INSTANCE =
+            new MainThreadInitializedObject<>(c -> new RecentsModel(c));
 
-    public static RecentsModel getInstance(final Context context) {
-        if (INSTANCE == null) {
-            if (Looper.myLooper() == Looper.getMainLooper()) {
-                INSTANCE = new RecentsModel(context.getApplicationContext());
-            } else {
-                try {
-                    return new MainThreadExecutor().submit(
-                            () -> RecentsModel.getInstance(context)).get();
-                } catch (InterruptedException|ExecutionException e) {
-                    throw new RuntimeException(e);
-                }
-            }
-        }
-        return INSTANCE;
-    }
-
-    private final SparseArray<Bundle> mCachedAssistData = new SparseArray<>(1);
-    private final ArrayList<AssistDataListener> mAssistDataListeners = new ArrayList<>();
-
+    private final List<TaskThumbnailChangeListener> mThumbnailChangeListeners = new ArrayList<>();
     private final Context mContext;
-    private final RecentsTaskLoader mRecentsTaskLoader;
-    private final MainThreadExecutor mMainThreadExecutor;
 
-    private RecentsTaskLoadPlan mLastLoadPlan;
-    private int mLastLoadPlanId;
-    private int mTaskChangeId;
     private ISystemUiProxy mSystemUiProxy;
-    private boolean mClearAssistCacheOnStackChange = true;
-    private final boolean mIsLowRamDevice;
-    private boolean mPreloadTasksInBackground;
-    private final AccessibilityManager mAccessibilityManager;
+
+    private final RecentTasksList mTaskList;
+    private final TaskIconCache mIconCache;
+    private final TaskThumbnailCache mThumbnailCache;
 
     private RecentsModel(Context context) {
         mContext = context;
-
-        ActivityManager activityManager =
-                (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
-        mIsLowRamDevice = activityManager.isLowRamDevice();
-        mMainThreadExecutor = new MainThreadExecutor();
-
-        Resources res = context.getResources();
-        mRecentsTaskLoader = new RecentsTaskLoader(mContext,
-                res.getInteger(R.integer.config_recentsMaxThumbnailCacheSize),
-                res.getInteger(R.integer.config_recentsMaxIconCacheSize), 0) {
-
-            @Override
-            protected IconLoader createNewIconLoader(Context context,
-                    TaskKeyLruCache<Drawable> iconCache,
-                    LruCache<ComponentName, ActivityInfo> activityInfoCache) {
-                return new NormalizedIconLoader(context, iconCache, activityInfoCache);
-            }
-        };
-        mRecentsTaskLoader.startLoader(mContext);
+        HandlerThread loaderThread = new HandlerThread("TaskThumbnailIconCache",
+                Process.THREAD_PRIORITY_BACKGROUND);
+        loaderThread.start();
+        mTaskList = new RecentTasksList(context);
+        mIconCache = new TaskIconCache(context, loaderThread.getLooper());
+        mThumbnailCache = new TaskThumbnailCache(context, loaderThread.getLooper());
         ActivityManagerWrapper.getInstance().registerTaskStackListener(this);
-
-        mTaskChangeId = 1;
-        loadTasks(-1, null);
-        mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
     }
 
-    public RecentsTaskLoader getRecentsTaskLoader() {
-        return mRecentsTaskLoader;
+    public TaskIconCache getIconCache() {
+        return mIconCache;
+    }
+
+    public TaskThumbnailCache getThumbnailCache() {
+        return mThumbnailCache;
     }
 
     /**
-     * Preloads the task plan
-     * @param taskId The running task id or -1
+     * Fetches the list of recent tasks.
+     *
      * @param callback The callback to receive the task plan once its complete or null. This is
      *                always called on the UI thread.
      * @return the request id associated with this call.
      */
-    public int loadTasks(int taskId, Consumer<RecentsTaskLoadPlan> callback) {
-        final int requestId = mTaskChangeId;
+    public int getTasks(Consumer<ArrayList<Task>> callback) {
+        return mTaskList.getTasks(false /* loadKeysOnly */, callback);
+    }
 
-        // Fail fast if nothing has changed.
-        if (mLastLoadPlanId == mTaskChangeId) {
-            if (callback != null) {
-                final RecentsTaskLoadPlan plan = mLastLoadPlan;
-                mMainThreadExecutor.execute(() -> callback.accept(plan));
-            }
-            return requestId;
-        }
+    /**
+     * @return The task id of the running task, or -1 if there is no current running task.
+     */
+    public static int getRunningTaskId() {
+        ActivityManager.RunningTaskInfo runningTask =
+                ActivityManagerWrapper.getInstance().getRunningTask();
+        return runningTask != null ? runningTask.id : -1;
+    }
 
-        BackgroundExecutor.get().submit(() -> {
-            // Preload the plan
-            RecentsTaskLoadPlan loadPlan = new RecentsTaskLoadPlan(mContext);
-            PreloadOptions opts = new PreloadOptions();
-            opts.loadTitles = mAccessibilityManager.isEnabled();
-            loadPlan.preloadPlan(opts, mRecentsTaskLoader, taskId, UserHandle.myUserId());
-            // Set the load plan on UI thread
-            mMainThreadExecutor.execute(() -> {
-                mLastLoadPlan = loadPlan;
-                mLastLoadPlanId = requestId;
+    /**
+     * @return Whether the provided {@param changeId} is the latest recent tasks list id.
+     */
+    public boolean isTaskListValid(int changeId) {
+        return mTaskList.isTaskListValid(changeId);
+    }
 
-                if (callback != null) {
-                    callback.accept(loadPlan);
+    /**
+     * 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.
+     */
+    public void findTaskWithId(int taskId, Consumer<Task.TaskKey> callback) {
+        mTaskList.getTasks(true /* loadKeysOnly */, (tasks) -> {
+            for (Task task : tasks) {
+                if (task.key.id == taskId) {
+                    callback.accept(task.key);
+                    return;
                 }
-            });
+            }
+            callback.accept(null);
         });
-        return requestId;
-    }
-
-    public void setPreloadTasksInBackground(boolean preloadTasksInBackground) {
-        mPreloadTasksInBackground = preloadTasksInBackground && !mIsLowRamDevice;
-    }
-
-    @Override
-    public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
-        mTaskChangeId++;
-    }
-
-    @Override
-    public void onActivityUnpinned() {
-        mTaskChangeId++;
-    }
-
-    @Override
-    public void onTaskStackChanged() {
-        mTaskChangeId++;
-
-        Preconditions.assertUIThread();
-        if (mClearAssistCacheOnStackChange) {
-            mCachedAssistData.clear();
-        } else {
-            mClearAssistCacheOnStackChange = true;
-        }
     }
 
     @Override
     public void onTaskStackChangedBackground() {
-        int userId = UserHandle.myUserId();
-        if (!mPreloadTasksInBackground || !checkCurrentOrManagedUserId(userId, mContext)) {
-            // TODO: Only register this for the current user
+        if (!mThumbnailCache.isPreloadingEnabled()) {
+            // Skip if we aren't preloading
             return;
         }
 
-        // Preload a fixed number of task icons/thumbnails in the background
-        ActivityManager.RunningTaskInfo runningTaskInfo =
-                ActivityManagerWrapper.getInstance().getRunningTask();
-        RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(mContext);
-        RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
-        launchOpts.runningTaskId = runningTaskInfo != null ? runningTaskInfo.id : -1;
-        launchOpts.numVisibleTasks = 2;
-        launchOpts.numVisibleTaskThumbnails = 2;
-        launchOpts.onlyLoadForCache = true;
-        launchOpts.onlyLoadPausedActivities = true;
-        launchOpts.loadThumbnails = true;
-        PreloadOptions preloadOpts = new PreloadOptions();
-        preloadOpts.loadTitles = mAccessibilityManager.isEnabled();
-        plan.preloadPlan(preloadOpts, mRecentsTaskLoader, -1, userId);
-        mRecentsTaskLoader.loadTasks(plan, launchOpts);
+        int currentUserId = Process.myUserHandle().getIdentifier();
+        if (!checkCurrentOrManagedUserId(currentUserId, mContext)) {
+            // Skip if we are not the current user
+            return;
+        }
+
+        // Keep the cache up to date with the latest thumbnails
+        int runningTaskId = RecentsModel.getRunningTaskId();
+        mTaskList.getTaskKeys(mThumbnailCache.getCacheSize(), tasks -> {
+            for (Task task : tasks) {
+                if (task.key.id == runningTaskId) {
+                    // Skip the running task, it's not going to have an up-to-date snapshot by the
+                    // time the user next enters overview
+                    continue;
+                }
+                mThumbnailCache.updateThumbnailInCache(task);
+            }
+        });
     }
 
-    public boolean isLoadPlanValid(int resultId) {
-        return mTaskChangeId == resultId;
+    @Override
+    public void onTaskSnapshotChanged(int taskId, ThumbnailData snapshot) {
+        mThumbnailCache.updateTaskSnapShot(taskId, snapshot);
+
+        for (int i = mThumbnailChangeListeners.size() - 1; i >= 0; i--) {
+            Task task = mThumbnailChangeListeners.get(i).onTaskThumbnailChanged(taskId, snapshot);
+            if (task != null) {
+                task.thumbnail = snapshot;
+            }
+        }
     }
 
-    public RecentsTaskLoadPlan getLastLoadPlan() {
-        return mLastLoadPlan;
+    @Override
+    public void onTaskRemoved(int taskId) {
+        Task.TaskKey dummyKey = new Task.TaskKey(taskId, 0, null, null, 0, 0);
+        mThumbnailCache.remove(dummyKey);
     }
 
     public void setSystemUiProxy(ISystemUiProxy systemUiProxy) {
@@ -230,16 +180,15 @@
         return mSystemUiProxy;
     }
 
-    public void onStart() {
-        mRecentsTaskLoader.startLoader(mContext);
-    }
-
     public void onTrimMemory(int level) {
         if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
-            // We already stop the loader in UI_HIDDEN, so stop the high res loader as well
-            mRecentsTaskLoader.getHighResThumbnailLoader().setVisible(false);
+            mThumbnailCache.getHighResLoadingState().setVisible(false);
         }
-        mRecentsTaskLoader.onTrimMemory(level);
+        if (level == ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL) {
+            // Clear everything once we reach a low-mem situation
+            mThumbnailCache.clear();
+            mIconCache.clear();
+        }
     }
 
     public void onOverviewShown(boolean fromHome, String tag) {
@@ -255,43 +204,16 @@
         }
     }
 
-    public void resetAssistCache() {
-        mCachedAssistData.clear();
+    public void addThumbnailChangeListener(TaskThumbnailChangeListener listener) {
+        mThumbnailChangeListeners.add(listener);
     }
 
-    @WorkerThread
-    public void preloadAssistData(int taskId, Bundle data) {
-        mMainThreadExecutor.execute(() -> {
-            mCachedAssistData.put(taskId, data);
-            // We expect a stack change callback after the assist data is set. So ignore the
-            // very next stack change callback.
-            mClearAssistCacheOnStackChange = false;
-
-            int count = mAssistDataListeners.size();
-            for (int i = 0; i < count; i++) {
-                mAssistDataListeners.get(i).onAssistDataReceived(taskId);
-            }
-        });
+    public void removeThumbnailChangeListener(TaskThumbnailChangeListener listener) {
+        mThumbnailChangeListeners.remove(listener);
     }
 
-    public Bundle getAssistData(int taskId) {
-        Preconditions.assertUIThread();
-        return mCachedAssistData.get(taskId);
-    }
+    public interface TaskThumbnailChangeListener {
 
-    public void addAssistDataListener(AssistDataListener listener) {
-        mAssistDataListeners.add(listener);
-    }
-
-    public void removeAssistDataListener(AssistDataListener listener) {
-        mAssistDataListeners.remove(listener);
-    }
-
-    /**
-     * Callback for receiving assist data
-     */
-    public interface AssistDataListener {
-
-        void onAssistDataReceived(int taskId);
+        Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData);
     }
 }
diff --git a/quickstep/src/com/android/quickstep/SysUINavigationMode.java b/quickstep/src/com/android/quickstep/SysUINavigationMode.java
new file mode 100644
index 0000000..b67c6f8
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/SysUINavigationMode.java
@@ -0,0 +1,129 @@
+/*
+ * 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.util.PackageManagerHelper.getPackageFilter;
+
+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.util.MainThreadInitializedObject;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Observer for the resource config that specifies the navigation bar mode.
+ */
+public class SysUINavigationMode {
+
+    public enum Mode {
+        THREE_BUTTONS(false, 0),
+        TWO_BUTTONS(true, 1),
+        NO_BUTTON(true, 2);
+
+        public final boolean hasGestures;
+        public final int resValue;
+
+        Mode(boolean hasGestures, int resValue) {
+            this.hasGestures = hasGestures;
+            this.resValue = resValue;
+        }
+    }
+
+    public static Mode getMode(Context context) {
+        return INSTANCE.get(context).getMode();
+    }
+
+    public static MainThreadInitializedObject<SysUINavigationMode> INSTANCE =
+            new MainThreadInitializedObject<>(SysUINavigationMode::new);
+
+    private static final String TAG = "SysUINavigationMode";
+
+    private final String ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED";
+    private static final String NAV_BAR_INTERACTION_MODE_RES_NAME =
+            "config_navBarInteractionMode";
+
+    private final Context mContext;
+    private Mode mMode;
+
+    private final List<NavigationModeChangeListener> mChangeListeners = new ArrayList<>();
+
+    public SysUINavigationMode(Context context) {
+        mContext = context;
+        initializeMode();
+
+        mContext.registerReceiver(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                Mode oldMode = mMode;
+                initializeMode();
+                if (mMode != oldMode) {
+                    dispatchModeChange();
+                }
+            }
+        }, getPackageFilter("android", ACTION_OVERLAY_CHANGED));
+    }
+
+    private void initializeMode() {
+        int modeInt = getSystemIntegerRes(mContext, NAV_BAR_INTERACTION_MODE_RES_NAME);
+        for(Mode m : Mode.values()) {
+            if (m.resValue == modeInt) {
+                mMode = m;
+            }
+        }
+    }
+
+    private void dispatchModeChange() {
+        for (NavigationModeChangeListener listener : mChangeListeners) {
+            listener.onNavigationModeChanged(mMode);
+        }
+    }
+
+    public Mode addModeChangeListener(NavigationModeChangeListener listener) {
+        mChangeListeners.add(listener);
+        return mMode;
+    }
+
+    public void removeModeChangeListener(NavigationModeChangeListener listener) {
+        mChangeListeners.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;
+        }
+    }
+
+    public interface NavigationModeChangeListener {
+
+        void onNavigationModeChanged(Mode newMode);
+    }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java
new file mode 100644
index 0000000..07af9b3
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.java
@@ -0,0 +1,157 @@
+/*
+ * 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.uioverrides.RecentsUiFactory.GO_LOW_RAM_RECENTS_ENABLED;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.LruCache;
+import android.view.accessibility.AccessibilityManager;
+
+import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.icons.cache.HandlerRunnable;
+import com.android.launcher3.uioverrides.RecentsUiFactory;
+import com.android.launcher3.util.Preconditions;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.TaskKeyLruCache;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+
+import java.util.function.Consumer;
+
+/**
+ * Manages the caching of task icons and related data.
+ * TODO: This class should later be merged into IconCache.
+ */
+public class TaskIconCache {
+
+    private final Handler mBackgroundHandler;
+    private final MainThreadExecutor mMainThreadExecutor;
+    private final AccessibilityManager mAccessibilityManager;
+
+    private final NormalizedIconLoader mIconLoader;
+
+    private final TaskKeyLruCache<Drawable> mIconCache;
+    private final TaskKeyLruCache<String> mContentDescriptionCache;
+    private final LruCache<ComponentName, ActivityInfo> mActivityInfoCache;
+
+    private TaskKeyLruCache.EvictionCallback mClearActivityInfoOnEviction =
+            new TaskKeyLruCache.EvictionCallback() {
+        @Override
+        public void onEntryEvicted(Task.TaskKey key) {
+            if (key != null) {
+                mActivityInfoCache.remove(key.getComponent());
+            }
+        }
+    };
+
+    public TaskIconCache(Context context, Looper backgroundLooper) {
+        mBackgroundHandler = new Handler(backgroundLooper);
+        mMainThreadExecutor = new MainThreadExecutor();
+        mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
+
+        Resources res = context.getResources();
+        int cacheSize = res.getInteger(R.integer.recentsIconCacheSize);
+        mIconCache = new TaskKeyLruCache<>(cacheSize, mClearActivityInfoOnEviction);
+        mContentDescriptionCache = new TaskKeyLruCache<>(cacheSize, mClearActivityInfoOnEviction);
+        mActivityInfoCache = new LruCache<>(cacheSize);
+        mIconLoader = new NormalizedIconLoader(context, mIconCache, mActivityInfoCache,
+                true /* disableColorExtraction */);
+    }
+
+    /**
+     * Asynchronously fetches the icon and other task data.
+     *
+     * @param task The task to fetch the data for
+     * @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) {
+        Preconditions.assertUIThread();
+        if (task.icon != null) {
+            // Nothing to load, the icon is already loaded
+            callback.accept(task);
+            return null;
+        }
+
+        IconLoadRequest request = new IconLoadRequest(mBackgroundHandler) {
+            @Override
+            public void run() {
+                Drawable icon = mIconLoader.getIcon(task);
+                String contentDescription = loadContentDescriptionInBackground(task);
+                if (isCanceled()) {
+                    // We don't call back to the provided callback in this case
+                    return;
+                }
+                mMainThreadExecutor.execute(() -> {
+                    task.icon = icon;
+                    task.titleDescription = contentDescription;
+                    callback.accept(task);
+                    onEnd();
+                });
+            }
+        };
+        Utilities.postAsyncCallback(mBackgroundHandler, request);
+        return request;
+    }
+
+    public void clear() {
+        mIconCache.evictAll();
+        mContentDescriptionCache.evictAll();
+    }
+
+    /**
+     * Loads the content description for the given {@param task}.
+     */
+    private String loadContentDescriptionInBackground(Task task) {
+        // Return the cached content description if it exists
+        String label = mContentDescriptionCache.getAndInvalidateIfModified(task.key);
+        if (label != null) {
+            return label;
+        }
+
+        // Skip loading content descriptions if accessibility is disabled unless low RAM recents
+        // is enabled.
+        if (!GO_LOW_RAM_RECENTS_ENABLED && !mAccessibilityManager.isEnabled()) {
+            return "";
+        }
+
+        // Skip loading the content description if the activity no longer exists
+        ActivityInfo activityInfo = mIconLoader.getAndUpdateActivityInfo(task.key);
+        if (activityInfo == null) {
+            return "";
+        }
+
+        // Load the label otherwise
+        label = ActivityManagerWrapper.getInstance().getBadgedContentDescription(activityInfo,
+                task.key.userId, task.taskDescription);
+        mContentDescriptionCache.put(task.key, label);
+        return label;
+    }
+
+    public static abstract class IconLoadRequest extends HandlerRunnable {
+        IconLoadRequest(Handler handler) {
+            super(handler, null);
+        }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
deleted file mode 100644
index 9d3ac6a..0000000
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ /dev/null
@@ -1,62 +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 android.content.Context;
-import android.graphics.Matrix;
-import android.support.annotation.AnyThread;
-import android.view.View;
-
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.util.Preconditions;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-
-/**
- * Factory class to create and add an overlays on the TaskView
- */
-public class TaskOverlayFactory {
-
-    private static TaskOverlayFactory sInstance;
-
-    public static TaskOverlayFactory get(Context context) {
-        Preconditions.assertUIThread();
-        if (sInstance == null) {
-            sInstance = Utilities.getOverrideObject(TaskOverlayFactory.class,
-                    context.getApplicationContext(), R.string.task_overlay_factory_class);
-        }
-        return sInstance;
-    }
-
-    @AnyThread
-    public boolean needAssist() {
-        return false;
-    }
-
-    public TaskOverlay createOverlay(View thumbnailView) {
-        return new TaskOverlay();
-    }
-
-    public static class TaskOverlay {
-
-        public void setTaskInfo(Task task, ThumbnailData thumbnail, Matrix matrix) { }
-
-        public void reset() { }
-
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/TaskSystemShortcut.java b/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
deleted file mode 100644
index e5a2b5e..0000000
--- a/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
+++ /dev/null
@@ -1,272 +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.userevent.nano.LauncherLogProto.Action.Touch.TAP;
-
-import android.content.ComponentName;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.util.Log;
-import android.view.View;
-
-import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.R;
-import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.popup.SystemShortcut;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.util.InstantAppResolver;
-import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskThumbnailView;
-import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.recents.ISystemUiProxy;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat;
-import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
-import com.android.systemui.shared.recents.view.RecentsTransition;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.ActivityOptionsCompat;
-import com.android.systemui.shared.system.WindowManagerWrapper;
-
-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 class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut {
-
-    private static final String TAG = "TaskSystemShortcut";
-
-    protected T mSystemShortcut;
-
-    protected TaskSystemShortcut(T systemShortcut) {
-        super(systemShortcut.iconResId, systemShortcut.labelResId);
-        mSystemShortcut = systemShortcut;
-    }
-
-    protected TaskSystemShortcut(int iconResId, int labelResId) {
-        super(iconResId, labelResId);
-    }
-
-    @Override
-    public View.OnClickListener getOnClickListener(
-            BaseDraggingActivity activity, ItemInfo itemInfo) {
-        return null;
-    }
-
-    public View.OnClickListener getOnClickListener(BaseDraggingActivity activity, TaskView view) {
-        Task task = view.getTask();
-
-        ShortcutInfo dummyInfo = new ShortcutInfo();
-        dummyInfo.intent = new Intent();
-        ComponentName component = task.getTopComponent();
-        dummyInfo.intent.setComponent(component);
-        dummyInfo.user = UserHandle.of(task.key.userId);
-        dummyInfo.title = TaskUtils.getTitle(activity, task);
-
-        return getOnClickListenerForTask(activity, task, dummyInfo);
-    }
-
-    protected View.OnClickListener getOnClickListenerForTask(
-            BaseDraggingActivity activity, Task task, ItemInfo dummyInfo) {
-        return mSystemShortcut.getOnClickListener(activity, dummyInfo);
-    }
-
-    public static class AppInfo extends TaskSystemShortcut<SystemShortcut.AppInfo> {
-        public AppInfo() {
-            super(new SystemShortcut.AppInfo());
-        }
-    }
-
-    public static class SplitScreen extends TaskSystemShortcut {
-
-        private Handler mHandler;
-
-        public SplitScreen() {
-            super(R.drawable.ic_split_screen, R.string.recent_task_option_split_screen);
-            mHandler = new Handler(Looper.getMainLooper());
-        }
-
-        @Override
-        public View.OnClickListener getOnClickListener(
-                BaseDraggingActivity activity, TaskView taskView) {
-            if (activity.getDeviceProfile().isMultiWindowMode) {
-                return null;
-            }
-            final Task task  = taskView.getTask();
-            final int taskId = task.key.id;
-            if (!task.isDockable) {
-                return null;
-            }
-            final RecentsView recentsView = activity.getOverviewPanel();
-
-            final TaskThumbnailView thumbnailView = taskView.getThumbnail();
-            return (v -> {
-                final View.OnLayoutChangeListener onLayoutChangeListener =
-                        new View.OnLayoutChangeListener() {
-                            @Override
-                            public void onLayoutChange(View v, int l, int t, int r, int b,
-                                    int oldL, int oldT, int oldR, int oldB) {
-                                taskView.getRootView().removeOnLayoutChangeListener(this);
-                                recentsView.removeIgnoreResetTask(taskView);
-
-                                // Start animating in the side pages once launcher has been resized
-                                recentsView.dismissTask(taskView, false, false);
-                            }
-                        };
-
-                final DeviceProfile.OnDeviceProfileChangeListener onDeviceProfileChangeListener =
-                        new DeviceProfile.OnDeviceProfileChangeListener() {
-                            @Override
-                            public void onDeviceProfileChanged(DeviceProfile dp) {
-                                activity.removeOnDeviceProfileChangeListener(this);
-                                if (dp.isMultiWindowMode) {
-                                    taskView.getRootView().addOnLayoutChangeListener(
-                                            onLayoutChangeListener);
-                                }
-                            }
-                        };
-
-                dismissTaskMenuView(activity);
-
-                final int navBarPosition = WindowManagerWrapper.getInstance().getNavBarPosition();
-                if (navBarPosition == WindowManagerWrapper.NAV_BAR_POS_INVALID) {
-                    return;
-                }
-                boolean dockTopOrLeft = navBarPosition != WindowManagerWrapper.NAV_BAR_POS_LEFT;
-                if (ActivityManagerWrapper.getInstance().startActivityFromRecents(taskId,
-                        ActivityOptionsCompat.makeSplitScreenOptions(dockTopOrLeft))) {
-                    ISystemUiProxy sysUiProxy = RecentsModel.getInstance(activity).getSystemUiProxy();
-                    try {
-                        sysUiProxy.onSplitScreenInvoked();
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "Failed to notify SysUI of split screen: ", e);
-                        return;
-                    }
-                    activity.getUserEventDispatcher().logActionOnControl(TAP,
-                            LauncherLogProto.ControlType.SPLIT_SCREEN_TARGET);
-                    // Add a device profile change listener to kick off animating the side tasks
-                    // once we enter multiwindow mode and relayout
-                    activity.addOnDeviceProfileChangeListener(onDeviceProfileChangeListener);
-
-                    final Runnable animStartedListener = () -> {
-                        // Hide the task view and wait for the window to be resized
-                        // TODO: Consider animating in launcher and do an in-place start activity
-                        //       afterwards
-                        recentsView.addIgnoreResetTask(taskView);
-                        taskView.setAlpha(0f);
-                    };
-
-                    final int[] position = new int[2];
-                    thumbnailView.getLocationOnScreen(position);
-                    final int width = (int) (thumbnailView.getWidth() * taskView.getScaleX());
-                    final int height = (int) (thumbnailView.getHeight() * taskView.getScaleY());
-                    final Rect taskBounds = new Rect(position[0], position[1],
-                            position[0] + width, position[1] + height);
-
-                    // Take the thumbnail of the task without a scrim and apply it back after
-                    float alpha = thumbnailView.getDimAlpha();
-                    thumbnailView.setDimAlpha(0);
-                    Bitmap thumbnail = RecentsTransition.drawViewIntoHardwareBitmap(
-                            taskBounds.width(), taskBounds.height(), thumbnailView, 1f,
-                            Color.BLACK);
-                    thumbnailView.setDimAlpha(alpha);
-
-                    AppTransitionAnimationSpecsFuture future =
-                            new AppTransitionAnimationSpecsFuture(mHandler) {
-                        @Override
-                        public List<AppTransitionAnimationSpecCompat> composeSpecs() {
-                            return Collections.singletonList(new AppTransitionAnimationSpecCompat(
-                                    taskId, thumbnail, taskBounds));
-                        }
-                    };
-                    WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture(
-                            future, animStartedListener, mHandler, true /* scaleUp */);
-                }
-            });
-        }
-    }
-
-    public static class Pin extends TaskSystemShortcut {
-
-        private static final String TAG = Pin.class.getSimpleName();
-
-        private Handler mHandler;
-
-        public Pin() {
-            super(R.drawable.ic_pin, R.string.recent_task_option_pin);
-            mHandler = new Handler(Looper.getMainLooper());
-        }
-
-        @Override
-        public View.OnClickListener getOnClickListener(
-                BaseDraggingActivity activity, TaskView taskView) {
-            ISystemUiProxy sysUiProxy = RecentsModel.getInstance(activity).getSystemUiProxy();
-            if (sysUiProxy == null) {
-                return null;
-            }
-            if (!ActivityManagerWrapper.getInstance().isScreenPinningEnabled()) {
-                return null;
-            }
-            if (ActivityManagerWrapper.getInstance().isLockToAppActive()) {
-                // We shouldn't be able to pin while an app is locked.
-                return null;
-            }
-            return view -> {
-                Consumer<Boolean> resultCallback = success -> {
-                    if (success) {
-                        try {
-                            sysUiProxy.startScreenPinning(taskView.getTask().key.id);
-                        } catch (RemoteException e) {
-                            Log.w(TAG, "Failed to start screen pinning: ", e);
-                        }
-                    } else {
-                        taskView.notifyTaskLaunchFailed(TAG);
-                    }
-                };
-                taskView.launchTask(true, resultCallback, mHandler);
-                dismissTaskMenuView(activity);
-            };
-        }
-    }
-
-    public static class Install extends TaskSystemShortcut<SystemShortcut.Install> {
-        public Install() {
-            super(new SystemShortcut.Install());
-        }
-
-        @Override
-        protected View.OnClickListener getOnClickListenerForTask(
-                BaseDraggingActivity activity, Task task, ItemInfo itemInfo) {
-            if (InstantAppResolver.newInstance(activity).isInstantApp(activity,
-                        task.getTopComponent().getPackageName())) {
-                return mSystemShortcut.createOnClickListener(activity, itemInfo);
-            }
-            return null;
-        }
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
new file mode 100644
index 0000000..57c5a27
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
@@ -0,0 +1,242 @@
+/*
+ * 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 android.app.ActivityManager;
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Handler;
+import android.os.Looper;
+import com.android.launcher3.MainThreadExecutor;
+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.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.Task.TaskKey;
+import com.android.systemui.shared.recents.model.TaskKeyLruCache;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import java.util.ArrayList;
+import java.util.function.Consumer;
+
+public class TaskThumbnailCache {
+
+    private final Handler mBackgroundHandler;
+    private final MainThreadExecutor mMainThreadExecutor;
+
+    private final int mCacheSize;
+    private final ThumbnailCache mCache;
+    private final HighResLoadingState mHighResLoadingState;
+
+    public static class HighResLoadingState {
+        private boolean mIsLowRamDevice;
+        private boolean mVisible;
+        private boolean mFlingingFast;
+        private boolean mHighResLoadingEnabled;
+        private ArrayList<HighResLoadingStateChangedCallback> mCallbacks = new ArrayList<>();
+
+        public interface HighResLoadingStateChangedCallback {
+            void onHighResLoadingStateChanged(boolean enabled);
+        }
+
+        private HighResLoadingState(Context context) {
+            ActivityManager activityManager =
+                    (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+            mIsLowRamDevice = activityManager.isLowRamDevice();
+        }
+
+        public void addCallback(HighResLoadingStateChangedCallback callback) {
+            mCallbacks.add(callback);
+        }
+
+        public void removeCallback(HighResLoadingStateChangedCallback callback) {
+            mCallbacks.remove(callback);
+        }
+
+        public void setVisible(boolean visible) {
+            mVisible = visible;
+            updateState();
+        }
+
+        public void setFlingingFast(boolean flingingFast) {
+            mFlingingFast = flingingFast;
+            updateState();
+        }
+
+        public boolean isEnabled() {
+            return mHighResLoadingEnabled;
+        }
+
+        private void updateState() {
+            boolean prevState = mHighResLoadingEnabled;
+            mHighResLoadingEnabled = !mIsLowRamDevice && mVisible && !mFlingingFast;
+            if (prevState != mHighResLoadingEnabled) {
+                for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+                    mCallbacks.get(i).onHighResLoadingStateChanged(mHighResLoadingEnabled);
+                }
+            }
+        }
+    }
+
+    public TaskThumbnailCache(Context context, Looper backgroundLooper) {
+        mBackgroundHandler = new Handler(backgroundLooper);
+        mMainThreadExecutor = new MainThreadExecutor();
+        mHighResLoadingState = new HighResLoadingState(context);
+
+        Resources res = context.getResources();
+        mCacheSize = res.getInteger(R.integer.recentsThumbnailCacheSize);
+        mCache = new ThumbnailCache(mCacheSize);
+    }
+
+    /**
+     * Synchronously fetches the thumbnail for the given {@param task} and puts it in the cache.
+     */
+    public void updateThumbnailInCache(Task task) {
+        Preconditions.assertUIThread();
+        // Fetch the thumbnail for this task and put it in the cache
+        if (task.thumbnail == null) {
+            updateThumbnailInBackground(task.key, true /* reducedResolution */,
+                    t -> task.thumbnail = t);
+        }
+    }
+
+    /**
+     * Synchronously updates the thumbnail in the cache if it is already there.
+     */
+    public void updateTaskSnapShot(int taskId, ThumbnailData thumbnail) {
+        Preconditions.assertUIThread();
+        mCache.updateIfAlreadyInCache(taskId, thumbnail);
+    }
+
+    /**
+     * Asynchronously fetches the icon and other task data for the given {@param task}.
+     *
+     * @param callback The callback to receive the task after its data has been populated.
+     * @return A cancelable handle to the request
+     */
+    public ThumbnailLoadRequest updateThumbnailInBackground(
+            Task task, Consumer<ThumbnailData> callback) {
+        Preconditions.assertUIThread();
+
+        boolean reducedResolution = !mHighResLoadingState.isEnabled();
+        if (task.thumbnail != null && (!task.thumbnail.reducedResolution || reducedResolution)) {
+            // Nothing to load, the thumbnail is already high-resolution or matches what the
+            // request, so just callback
+            callback.accept(task.thumbnail);
+            return null;
+        }
+
+
+        return updateThumbnailInBackground(task.key, !mHighResLoadingState.isEnabled(), t -> {
+            task.thumbnail = t;
+            callback.accept(t);
+        });
+    }
+
+    private ThumbnailLoadRequest updateThumbnailInBackground(TaskKey key, boolean reducedResolution,
+            Consumer<ThumbnailData> callback) {
+        Preconditions.assertUIThread();
+
+        ThumbnailData cachedThumbnail = mCache.getAndInvalidateIfModified(key);
+        if (cachedThumbnail != null && (!cachedThumbnail.reducedResolution || reducedResolution)) {
+            // Already cached, lets use that thumbnail
+            callback.accept(cachedThumbnail);
+            return null;
+        }
+
+        ThumbnailLoadRequest request = new ThumbnailLoadRequest(mBackgroundHandler,
+                reducedResolution) {
+            @Override
+            public void run() {
+                ThumbnailData thumbnail = ActivityManagerWrapper.getInstance().getTaskThumbnail(
+                        key.id, reducedResolution);
+                if (isCanceled()) {
+                    // We don't call back to the provided callback in this case
+                    return;
+                }
+                mMainThreadExecutor.execute(() -> {
+                    mCache.put(key, thumbnail);
+                    callback.accept(thumbnail);
+                    onEnd();
+                });
+            }
+        };
+        Utilities.postAsyncCallback(mBackgroundHandler, request);
+        return request;
+    }
+
+    /**
+     * Clears the cache.
+     */
+    public void clear() {
+        mCache.evictAll();
+    }
+
+    /**
+     * Removes the cached thumbnail for the given task.
+     */
+    public void remove(Task.TaskKey key) {
+        mCache.remove(key);
+    }
+
+    /**
+     * @return The cache size.
+     */
+    public int getCacheSize() {
+        return mCacheSize;
+    }
+
+    /**
+     * @return The mutable high-res loading state.
+     */
+    public HighResLoadingState getHighResLoadingState() {
+        return mHighResLoadingState;
+    }
+
+    /**
+     * @return Whether to enable background preloading of task thumbnails.
+     */
+    public boolean isPreloadingEnabled() {
+        return !mHighResLoadingState.mIsLowRamDevice && mHighResLoadingState.mVisible;
+    }
+
+    public static abstract class ThumbnailLoadRequest extends HandlerRunnable {
+        public final boolean reducedResolution;
+
+        ThumbnailLoadRequest(Handler handler, boolean reducedResolution) {
+            super(handler, null);
+            this.reducedResolution = reducedResolution;
+        }
+    }
+
+    private static class ThumbnailCache extends TaskKeyLruCache<ThumbnailData> {
+
+        public ThumbnailCache(int cacheSize) {
+            super(cacheSize);
+        }
+
+        /**
+         * Updates the cache entry if it is already present in the cache
+         */
+        public void updateIfAlreadyInCache(int taskId, ThumbnailData thumbnailData) {
+            ThumbnailData oldData = getCacheEntry(taskId);
+            if (oldData != null) {
+                putCacheEntry(taskId, thumbnailData);
+            }
+        }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/TaskUtils.java b/quickstep/src/com/android/quickstep/TaskUtils.java
index f9b5e30..5f76ca7 100644
--- a/quickstep/src/com/android/quickstep/TaskUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskUtils.java
@@ -16,53 +16,35 @@
 
 package com.android.quickstep;
 
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
-import static com.android.systemui.shared.recents.utilities.Utilities.getNextFrameNumber;
-import static com.android.systemui.shared.recents.utilities.Utilities.getSurface;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
-
-import android.animation.ValueAnimator;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.graphics.RectF;
 import android.os.UserHandle;
 import android.util.Log;
-import android.view.Surface;
-import android.view.View;
 
-import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.util.ComponentKey;
-import com.android.quickstep.util.ClipAnimationHelper;
-import com.android.quickstep.util.MultiValueUpdateListener;
-import com.android.quickstep.util.RemoteAnimationTargetSet;
-import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier;
 
 import java.util.List;
 
 /**
  * Contains helpful methods for retrieving data from {@link Task}s.
  */
-public class TaskUtils {
+public final class TaskUtils {
 
     private static final String TAG = "TaskUtils";
 
+    private TaskUtils() {}
+
     /**
      * TODO: remove this once we switch to getting the icon and label from IconCache.
      */
     public static CharSequence getTitle(Context context, Task task) {
         LauncherAppsCompat launcherAppsCompat = LauncherAppsCompat.getInstance(context);
-        UserManagerCompat userManagerCompat = UserManagerCompat.getInstance(context);
         PackageManager packageManager = context.getPackageManager();
         UserHandle user = UserHandle.of(task.key.userId);
         ApplicationInfo applicationInfo = launcherAppsCompat.getApplicationInfo(
@@ -71,7 +53,7 @@
             Log.e(TAG, "Failed to get title for task " + task);
             return "";
         }
-        return userManagerCompat.getBadgedLabelForUser(
+        return packageManager.getUserBadgedLabel(
             applicationInfo.loadLabel(packageManager), user);
     }
 
@@ -83,115 +65,6 @@
     }
 
 
-    /**
-     * 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) {
-        if (v instanceof TaskView) {
-            return (TaskView) v;
-        }
-        RecentsView recentsView = activity.getOverviewPanel();
-
-        // 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;
-    }
-
-    /**
-     * @return Animator that controls the window of the opening targets for the recents launch
-     * animation.
-     */
-    public static ValueAnimator getRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
-            RemoteAnimationTargetCompat[] targets, final ClipAnimationHelper inOutHelper) {
-        SyncRtSurfaceTransactionApplier syncTransactionApplier =
-                new SyncRtSurfaceTransactionApplier(v);
-        final ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
-        appAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
-        appAnimator.addUpdateListener(new MultiValueUpdateListener() {
-
-            // Defer fading out the view until after the app window gets faded in
-            final FloatProp mViewAlpha = new FloatProp(1f, 0f, 75, 75, LINEAR);
-            final FloatProp mTaskAlpha = new FloatProp(0f, 1f, 0, 75, LINEAR);
-
-            final RemoteAnimationTargetSet mTargetSet;
-
-            final RectF mThumbnailRect;
-
-            {
-                mTargetSet = new RemoteAnimationTargetSet(targets, MODE_OPENING);
-                inOutHelper.setTaskAlphaCallback((t, alpha) -> mTaskAlpha.value);
-
-                inOutHelper.prepareAnimation(true /* isOpening */);
-                inOutHelper.fromTaskThumbnailView(v.getThumbnail(), (RecentsView) v.getParent(),
-                        mTargetSet.apps.length == 0 ? null : mTargetSet.apps[0]);
-
-                mThumbnailRect = new RectF(inOutHelper.getTargetRect());
-                mThumbnailRect.offset(-v.getTranslationX(), -v.getTranslationY());
-                Utilities.scaleRectFAboutCenter(mThumbnailRect, 1 / v.getScaleX());
-            }
-
-            @Override
-            public void onUpdate(float percent) {
-                RectF taskBounds = inOutHelper.applyTransform(mTargetSet, 1 - percent,
-                        syncTransactionApplier);
-                if (!skipViewChanges) {
-                    float scale = taskBounds.width() / mThumbnailRect.width();
-                    v.setScaleX(scale);
-                    v.setScaleY(scale);
-                    v.setTranslationX(taskBounds.centerX() - mThumbnailRect.centerX());
-                    v.setTranslationY(taskBounds.centerY() - mThumbnailRect.centerY());
-                    v.setAlpha(mViewAlpha.value);
-                }
-            }
-        });
-        return appAnimator;
-    }
-
     public static boolean taskIsATargetWithMode(RemoteAnimationTargetCompat[] targets,
             int taskId, int mode) {
         for (RemoteAnimationTargetCompat target : targets) {
diff --git a/quickstep/src/com/android/quickstep/TouchConsumer.java b/quickstep/src/com/android/quickstep/TouchConsumer.java
deleted file mode 100644
index 4cecffa..0000000
--- a/quickstep/src/com/android/quickstep/TouchConsumer.java
+++ /dev/null
@@ -1,74 +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 android.annotation.TargetApi;
-import android.os.Build;
-import android.support.annotation.IntDef;
-import android.view.Choreographer;
-import android.view.MotionEvent;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.function.Consumer;
-
-@TargetApi(Build.VERSION_CODES.O)
-@FunctionalInterface
-public interface TouchConsumer extends Consumer<MotionEvent> {
-
-    @IntDef(flag = true, value = {
-            INTERACTION_NORMAL,
-            INTERACTION_QUICK_SCRUB
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    @interface InteractionType {}
-    int INTERACTION_NORMAL = 0;
-    int INTERACTION_QUICK_SCRUB = 1;
-
-    default void reset() { }
-
-    default void updateTouchTracking(@InteractionType int interactionType) { }
-
-    default void onQuickScrubEnd() { }
-
-    default void onQuickScrubProgress(float progress) { }
-
-    default void onQuickStep(MotionEvent ev) { }
-
-    default void onCommand(int command) { }
-
-    /**
-     * Called on the binder thread to allow the consumer to process the motion event before it is
-     * posted on a handler thread.
-     */
-    default void preProcessMotionEvent(MotionEvent ev) { }
-
-    default Choreographer getIntrimChoreographer(MotionEventQueue queue) {
-        return null;
-    }
-
-    default void deferInit() { }
-
-    default boolean deferNextEventToMainThread() {
-        return false;
-    }
-
-    default boolean forceToLauncherConsumer() {
-        return false;
-    }
-
-    default void onShowOverviewFromAltTab() {}
-}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
deleted file mode 100644
index 6c54262..0000000
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ /dev/null
@@ -1,414 +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;
-
-import static android.view.MotionEvent.ACTION_CANCEL;
-import static android.view.MotionEvent.ACTION_DOWN;
-import static android.view.MotionEvent.ACTION_MOVE;
-import static android.view.MotionEvent.ACTION_POINTER_DOWN;
-import static android.view.MotionEvent.ACTION_POINTER_UP;
-import static android.view.MotionEvent.ACTION_UP;
-import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
-import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
-
-import android.annotation.TargetApi;
-import android.app.ActivityManager.RunningTaskInfo;
-import android.app.Service;
-import android.content.Intent;
-import android.graphics.PointF;
-import android.os.Build;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.Choreographer;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.ViewConfiguration;
-
-import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.MainThreadExecutor;
-import com.android.launcher3.util.TraceHelper;
-import com.android.launcher3.views.BaseDragLayer;
-import com.android.quickstep.views.RecentsView;
-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.ChoreographerCompat;
-import com.android.systemui.shared.system.NavigationBarCompat.HitTarget;
-
-/**
- * Service connected by system-UI for handling touch interaction.
- */
-@TargetApi(Build.VERSION_CODES.O)
-public class TouchInteractionService extends Service {
-
-    private static final SparseArray<String> sMotionEventNames;
-
-    static {
-        sMotionEventNames = new SparseArray<>(3);
-        sMotionEventNames.put(ACTION_DOWN, "ACTION_DOWN");
-        sMotionEventNames.put(ACTION_UP, "ACTION_UP");
-        sMotionEventNames.put(ACTION_CANCEL, "ACTION_CANCEL");
-    }
-
-    public static final int EDGE_NAV_BAR = 1 << 8;
-
-    private static final String TAG = "TouchInteractionService";
-
-    /**
-     * A background thread used for handling UI for another window.
-     */
-    private static HandlerThread sRemoteUiThread;
-
-    private final IBinder mMyBinder = new IOverviewProxy.Stub() {
-
-        @Override
-        public void onPreMotionEvent(@HitTarget int downHitTarget) throws RemoteException {
-            TraceHelper.beginSection("SysUiBinder");
-            setupTouchConsumer(downHitTarget);
-            TraceHelper.partitionSection("SysUiBinder", "Down target " + downHitTarget);
-        }
-
-        @Override
-        public void onMotionEvent(MotionEvent ev) {
-            mEventQueue.queue(ev);
-
-            String name = sMotionEventNames.get(ev.getActionMasked());
-            if (name != null){
-                TraceHelper.partitionSection("SysUiBinder", name);
-            }
-        }
-
-        @Override
-        public void onBind(ISystemUiProxy iSystemUiProxy) {
-            mISystemUiProxy = iSystemUiProxy;
-            mRecentsModel.setSystemUiProxy(mISystemUiProxy);
-            mOverviewInteractionState.setSystemUiProxy(mISystemUiProxy);
-        }
-
-        @Override
-        public void onQuickScrubStart() {
-            mEventQueue.onQuickScrubStart();
-            TraceHelper.partitionSection("SysUiBinder", "onQuickScrubStart");
-        }
-
-        @Override
-        public void onQuickScrubProgress(float progress) {
-            mEventQueue.onQuickScrubProgress(progress);
-        }
-
-        @Override
-        public void onQuickScrubEnd() {
-            mEventQueue.onQuickScrubEnd();
-            TraceHelper.endSection("SysUiBinder", "onQuickScrubEnd");
-        }
-
-        @Override
-        public void onOverviewToggle() {
-            mOverviewCommandHelper.onOverviewToggle();
-        }
-
-        @Override
-        public void onOverviewShown(boolean triggeredFromAltTab) {
-            if (triggeredFromAltTab) {
-                setupTouchConsumer(HIT_TARGET_NONE);
-                mEventQueue.onOverviewShownFromAltTab();
-            } else {
-                mOverviewCommandHelper.onOverviewShown();
-            }
-        }
-
-        @Override
-        public void onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
-            if (triggeredFromAltTab && !triggeredFromHomeKey) {
-                // onOverviewShownFromAltTab initiates quick scrub. Ending it here.
-                mEventQueue.onQuickScrubEnd();
-            }
-        }
-
-        @Override
-        public void onQuickStep(MotionEvent motionEvent) {
-            mEventQueue.onQuickStep(motionEvent);
-            TraceHelper.endSection("SysUiBinder", "onQuickStep");
-
-        }
-
-        @Override
-        public void onTip(int actionType, int viewType) {
-            mOverviewCommandHelper.onTip(actionType, viewType);
-        }
-    };
-
-    private final TouchConsumer mNoOpTouchConsumer = (ev) -> {};
-
-    private static boolean sConnected = false;
-
-    public static boolean isConnected() {
-        return sConnected;
-    }
-
-    private ActivityManagerWrapper mAM;
-    private RecentsModel mRecentsModel;
-    private MotionEventQueue mEventQueue;
-    private MainThreadExecutor mMainThreadExecutor;
-    private ISystemUiProxy mISystemUiProxy;
-    private OverviewCommandHelper mOverviewCommandHelper;
-    private OverviewInteractionState mOverviewInteractionState;
-    private OverviewCallbacks mOverviewCallbacks;
-    private TaskOverlayFactory mTaskOverlayFactory;
-
-    private Choreographer mMainThreadChoreographer;
-    private Choreographer mBackgroundThreadChoreographer;
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        mAM = ActivityManagerWrapper.getInstance();
-        mRecentsModel = RecentsModel.getInstance(this);
-        mRecentsModel.setPreloadTasksInBackground(true);
-        mMainThreadExecutor = new MainThreadExecutor();
-        mOverviewCommandHelper = new OverviewCommandHelper(this);
-        mMainThreadChoreographer = Choreographer.getInstance();
-        mEventQueue = new MotionEventQueue(mMainThreadChoreographer, mNoOpTouchConsumer);
-        mOverviewInteractionState = OverviewInteractionState.getInstance(this);
-        mOverviewCallbacks = OverviewCallbacks.get(this);
-        mTaskOverlayFactory = TaskOverlayFactory.get(this);
-
-        sConnected = true;
-
-        // Temporarily disable model preload
-        // new ModelPreload().start(this);
-        initBackgroundChoreographer();
-    }
-
-    @Override
-    public void onDestroy() {
-        mOverviewCommandHelper.onDestroy();
-        sConnected = false;
-        super.onDestroy();
-    }
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        Log.d(TAG, "Touch service connected");
-        return mMyBinder;
-    }
-
-    private void setupTouchConsumer(@HitTarget int downHitTarget) {
-        mEventQueue.reset();
-        TouchConsumer oldConsumer = mEventQueue.getConsumer();
-        if (oldConsumer.deferNextEventToMainThread()) {
-            mEventQueue = new MotionEventQueue(mMainThreadChoreographer,
-                    new DeferredTouchConsumer((v) -> getCurrentTouchConsumer(downHitTarget,
-                            oldConsumer.forceToLauncherConsumer(), v)));
-            mEventQueue.deferInit();
-        } else {
-            mEventQueue = new MotionEventQueue(
-                    mMainThreadChoreographer, getCurrentTouchConsumer(downHitTarget, false, null));
-        }
-    }
-
-    private TouchConsumer getCurrentTouchConsumer(
-            @HitTarget int downHitTarget, boolean forceToLauncher, VelocityTracker tracker) {
-        RunningTaskInfo runningTaskInfo = mAM.getRunningTask(0);
-
-        if (runningTaskInfo == null && !forceToLauncher) {
-            return mNoOpTouchConsumer;
-        } else if (forceToLauncher ||
-                runningTaskInfo.topActivity.equals(mOverviewCommandHelper.overviewComponent)) {
-            return getOverviewConsumer();
-        } else {
-            if (tracker == null) {
-                tracker = VelocityTracker.obtain();
-            }
-            return new OtherActivityTouchConsumer(this, runningTaskInfo, mRecentsModel,
-                            mOverviewCommandHelper.overviewIntent,
-                            mOverviewCommandHelper.getActivityControlHelper(), mMainThreadExecutor,
-                            mBackgroundThreadChoreographer, downHitTarget, mOverviewCallbacks,
-                            mTaskOverlayFactory, tracker);
-        }
-    }
-
-    private TouchConsumer getOverviewConsumer() {
-        ActivityControlHelper activityHelper = mOverviewCommandHelper.getActivityControlHelper();
-        BaseDraggingActivity activity = activityHelper.getCreatedActivity();
-        if (activity == null) {
-            return mNoOpTouchConsumer;
-        }
-        return new OverviewTouchConsumer(activityHelper, activity);
-    }
-
-    private static class OverviewTouchConsumer<T extends BaseDraggingActivity>
-            implements TouchConsumer {
-
-        private final ActivityControlHelper<T> mActivityHelper;
-        private final T mActivity;
-        private final BaseDragLayer mTarget;
-        private final int[] mLocationOnScreen = new int[2];
-        private final PointF mDownPos = new PointF();
-        private final int mTouchSlop;
-        private final QuickScrubController mQuickScrubController;
-
-        private boolean mTrackingStarted = false;
-        private boolean mInvalidated = false;
-
-        private float mLastProgress = 0;
-        private boolean mStartPending = false;
-        private boolean mEndPending = false;
-
-        OverviewTouchConsumer(ActivityControlHelper<T> activityHelper, T activity) {
-            mActivityHelper = activityHelper;
-            mActivity = activity;
-            mTarget = activity.getDragLayer();
-            mTouchSlop = ViewConfiguration.get(mTarget.getContext()).getScaledTouchSlop();
-
-            mQuickScrubController = mActivity.<RecentsView>getOverviewPanel()
-                    .getQuickScrubController();
-        }
-
-        @Override
-        public void accept(MotionEvent ev) {
-            if (mInvalidated) {
-                return;
-            }
-            int action = ev.getActionMasked();
-            if (action == ACTION_DOWN) {
-                mTrackingStarted = false;
-                mDownPos.set(ev.getX(), ev.getY());
-            } else if (!mTrackingStarted) {
-                switch (action) {
-                    case ACTION_POINTER_UP:
-                    case ACTION_POINTER_DOWN:
-                        if (!mTrackingStarted) {
-                            mInvalidated = true;
-                        }
-                        break;
-                    case ACTION_MOVE: {
-                        float displacement = ev.getY() - mDownPos.y;
-                        if (Math.abs(displacement) >= mTouchSlop) {
-                            mTarget.getLocationOnScreen(mLocationOnScreen);
-
-                            // Send a down event only when mTouchSlop is crossed.
-                            MotionEvent down = MotionEvent.obtain(ev);
-                            down.setAction(ACTION_DOWN);
-                            sendEvent(down);
-                            down.recycle();
-                            mTrackingStarted = true;
-                        }
-                    }
-                }
-            }
-
-            if (mTrackingStarted) {
-                sendEvent(ev);
-            }
-
-            if (action == ACTION_UP || action == ACTION_CANCEL) {
-                mInvalidated = true;
-            }
-        }
-
-        private void sendEvent(MotionEvent ev) {
-            int flags = ev.getEdgeFlags();
-            ev.setEdgeFlags(flags | EDGE_NAV_BAR);
-            ev.offsetLocation(-mLocationOnScreen[0], -mLocationOnScreen[1]);
-            if (!mTrackingStarted) {
-                mTarget.onInterceptTouchEvent(ev);
-            }
-            mTarget.onTouchEvent(ev);
-            ev.offsetLocation(mLocationOnScreen[0], mLocationOnScreen[1]);
-            ev.setEdgeFlags(flags);
-        }
-
-        @Override
-        public void onQuickStep(MotionEvent ev) {
-            if (mInvalidated) {
-                return;
-            }
-            OverviewCallbacks.get(mActivity).closeAllWindows();
-            ActivityManagerWrapper.getInstance()
-                    .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
-        }
-
-        @Override
-        public void updateTouchTracking(int interactionType) {
-            if (mInvalidated) {
-                return;
-            }
-            if (interactionType == INTERACTION_QUICK_SCRUB) {
-                if (!mQuickScrubController.prepareQuickScrub(TAG)) {
-                    mInvalidated = true;
-                    return;
-                }
-                OverviewCallbacks.get(mActivity).closeAllWindows();
-                ActivityManagerWrapper.getInstance()
-                        .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
-
-                mStartPending = true;
-                Runnable action = () -> {
-                    if (!mQuickScrubController.prepareQuickScrub(TAG)) {
-                        mInvalidated = true;
-                        return;
-                    }
-                    mActivityHelper.onQuickInteractionStart(mActivity, null, true);
-                    mQuickScrubController.onQuickScrubProgress(mLastProgress);
-                    mStartPending = false;
-
-                    if (mEndPending) {
-                        mQuickScrubController.onQuickScrubEnd();
-                        mEndPending = false;
-                    }
-                };
-
-                mActivityHelper.executeOnWindowAvailable(mActivity, action);
-            }
-        }
-
-        @Override
-        public void onQuickScrubEnd() {
-            if (mInvalidated) {
-                return;
-            }
-            if (mStartPending) {
-                mEndPending = true;
-            } else {
-                mQuickScrubController.onQuickScrubEnd();
-            }
-        }
-
-        @Override
-        public void onQuickScrubProgress(float progress) {
-            mLastProgress = progress;
-            if (mInvalidated || mStartPending) {
-                return;
-            }
-            mQuickScrubController.onQuickScrubProgress(progress);
-        }
-
-    }
-
-    private void initBackgroundChoreographer() {
-        if (sRemoteUiThread == null) {
-            sRemoteUiThread = new HandlerThread("remote-ui");
-            sRemoteUiThread.start();
-        }
-        new Handler(sRemoteUiThread.getLooper()).post(() ->
-                mBackgroundThreadChoreographer = ChoreographerCompat.getSfInstance());
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
deleted file mode 100644
index 1f0a057..0000000
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ /dev/null
@@ -1,1102 +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.Utilities.SINGLE_FRAME_MS;
-import static com.android.launcher3.Utilities.postAsyncCallback;
-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.quickstep.QuickScrubController.QUICK_SCRUB_FROM_APP_START_DURATION;
-import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
-import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
-import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.annotation.TargetApi;
-import android.app.ActivityManager.RunningTaskInfo;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.support.annotation.AnyThread;
-import android.support.annotation.UiThread;
-import android.support.annotation.WorkerThread;
-import android.util.Log;
-import android.view.HapticFeedbackConstants;
-import android.view.View;
-import android.view.ViewTreeObserver.OnDrawListener;
-import android.view.WindowManager;
-import android.view.animation.Interpolator;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.LauncherAppState;
-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.Interpolators;
-import com.android.launcher3.logging.UserEventDispatcher;
-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.MultiValueAlpha;
-import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
-import com.android.launcher3.util.TraceHelper;
-import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
-import com.android.quickstep.ActivityControlHelper.AnimationFactory;
-import com.android.quickstep.ActivityControlHelper.LayoutListener;
-import com.android.quickstep.TouchConsumer.InteractionType;
-import com.android.quickstep.util.ClipAnimationHelper;
-import com.android.quickstep.util.RemoteAnimationTargetSet;
-import com.android.quickstep.util.TransformedRect;
-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.RecentsAnimationControllerCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier;
-import com.android.systemui.shared.system.WindowCallbacksCompat;
-import com.android.systemui.shared.system.WindowManagerWrapper;
-
-import java.util.StringJoiner;
-import java.util.function.BiFunction;
-
-@TargetApi(Build.VERSION_CODES.O)
-public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
-    private static final String TAG = WindowTransformSwipeHandler.class.getSimpleName();
-    private static final boolean DEBUG_STATES = false;
-
-    // Launcher UI related states
-    private static final int STATE_LAUNCHER_PRESENT = 1 << 0;
-    private static final int STATE_LAUNCHER_STARTED = 1 << 1;
-    private static final int STATE_LAUNCHER_DRAWN = 1 << 2;
-    private static final int STATE_ACTIVITY_MULTIPLIER_COMPLETE = 1 << 3;
-
-    // Internal initialization states
-    private static final int STATE_APP_CONTROLLER_RECEIVED = 1 << 4;
-
-    // Interaction finish states
-    private static final int STATE_SCALED_CONTROLLER_RECENTS = 1 << 5;
-    private static final int STATE_SCALED_CONTROLLER_APP = 1 << 6;
-
-    private static final int STATE_HANDLER_INVALIDATED = 1 << 7;
-    private static final int STATE_GESTURE_STARTED_QUICKSTEP = 1 << 8;
-    private static final int STATE_GESTURE_STARTED_QUICKSCRUB = 1 << 9;
-    private static final int STATE_GESTURE_CANCELLED = 1 << 10;
-    private static final int STATE_GESTURE_COMPLETED = 1 << 11;
-
-    // States for quick switch/scrub
-    private static final int STATE_CURRENT_TASK_FINISHED = 1 << 12;
-    private static final int STATE_QUICK_SCRUB_START = 1 << 13;
-    private static final int STATE_QUICK_SCRUB_END = 1 << 14;
-
-    private static final int STATE_CAPTURE_SCREENSHOT = 1 << 15;
-    private static final int STATE_SCREENSHOT_CAPTURED = 1 << 16;
-
-    private static final int STATE_RESUME_LAST_TASK = 1 << 17;
-    private static final int STATE_ASSIST_DATA_RECEIVED = 1 << 18;
-
-    private static final int LAUNCHER_UI_STATES =
-            STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_ACTIVITY_MULTIPLIER_COMPLETE
-            | STATE_LAUNCHER_STARTED;
-
-    private static final int LONG_SWIPE_ENTER_STATE =
-            STATE_ACTIVITY_MULTIPLIER_COMPLETE | STATE_LAUNCHER_STARTED
-                    | STATE_APP_CONTROLLER_RECEIVED;
-
-    private static final int LONG_SWIPE_START_STATE =
-            STATE_ACTIVITY_MULTIPLIER_COMPLETE | STATE_LAUNCHER_STARTED
-                    | STATE_APP_CONTROLLER_RECEIVED | STATE_SCREENSHOT_CAPTURED;
-
-    // For debugging, keep in sync with above states
-    private static final String[] STATES = new String[] {
-            "STATE_LAUNCHER_PRESENT",
-            "STATE_LAUNCHER_STARTED",
-            "STATE_LAUNCHER_DRAWN",
-            "STATE_ACTIVITY_MULTIPLIER_COMPLETE",
-            "STATE_APP_CONTROLLER_RECEIVED",
-            "STATE_SCALED_CONTROLLER_RECENTS",
-            "STATE_SCALED_CONTROLLER_APP",
-            "STATE_HANDLER_INVALIDATED",
-            "STATE_GESTURE_STARTED_QUICKSTEP",
-            "STATE_GESTURE_STARTED_QUICKSCRUB",
-            "STATE_GESTURE_CANCELLED",
-            "STATE_GESTURE_COMPLETED",
-            "STATE_CURRENT_TASK_FINISHED",
-            "STATE_QUICK_SCRUB_START",
-            "STATE_QUICK_SCRUB_END",
-            "STATE_CAPTURE_SCREENSHOT",
-            "STATE_SCREENSHOT_CAPTURED",
-            "STATE_RESUME_LAST_TASK",
-            "STATE_ASSIST_DATA_RECEIVED",
-    };
-
-    public static final long MAX_SWIPE_DURATION = 350;
-    public static final long MIN_SWIPE_DURATION = 80;
-    public static final long MIN_OVERSHOOT_DURATION = 120;
-
-    public static final float MIN_PROGRESS_FOR_OVERVIEW = 0.5f;
-    private static final float SWIPE_DURATION_MULTIPLIER =
-            Math.min(1 / MIN_PROGRESS_FOR_OVERVIEW, 1 / (1 - MIN_PROGRESS_FOR_OVERVIEW));
-
-    private final ClipAnimationHelper mClipAnimationHelper = new ClipAnimationHelper();
-
-    protected Runnable mGestureEndCallback;
-    protected boolean mIsGoingToHome;
-    private DeviceProfile mDp;
-    private int mTransitionDragLength;
-
-    // Shift in the range of [0, 1].
-    // 0 => preview snapShot is completely visible, and hotseat is completely translated down
-    // 1 => preview snapShot is completely aligned with the recents view and hotseat is completely
-    // visible.
-    private final AnimatedFloat mCurrentShift = new AnimatedFloat(this::updateFinalShift);
-
-    private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
-
-    // An increasing identifier per single instance of OtherActivityTouchConsumer. Generally one
-    // instance of OtherActivityTouchConsumer will only have one swipe handle, but sometimes we can
-    // end up with multiple handlers if we get recents command in the middle of a swipe gesture.
-    // This is used to match the corresponding activity manager callbacks in
-    // OtherActivityTouchConsumer
-    public final int id;
-    private final Context mContext;
-    private final ActivityControlHelper<T> mActivityControlHelper;
-    private final ActivityInitListener mActivityInitListener;
-
-    private final int mRunningTaskId;
-    private final RunningTaskInfo mRunningTaskInfo;
-    private ThumbnailData mTaskSnapshot;
-
-    private MultiStateCallback mStateCallback;
-    private AnimatorPlaybackController mLauncherTransitionController;
-
-    private T mActivity;
-    private LayoutListener mLayoutListener;
-    private RecentsView mRecentsView;
-    private SyncRtSurfaceTransactionApplier mSyncTransactionApplier;
-    private QuickScrubController mQuickScrubController;
-    private AnimationFactory mAnimationFactory = (t, i) -> { };
-
-    private Runnable mLauncherDrawnCallback;
-
-    private boolean mWasLauncherAlreadyVisible;
-
-    private boolean mPassedOverviewThreshold;
-    private boolean mGestureStarted;
-    private int mLogAction = Touch.SWIPE;
-    private float mCurrentQuickScrubProgress;
-    private boolean mQuickScrubBlocked;
-
-    private @InteractionType int mInteractionType = INTERACTION_NORMAL;
-
-    private InputConsumerController mInputConsumer =
-            InputConsumerController.getRecentsAnimationInputConsumer();
-
-    private final RecentsAnimationWrapper mRecentsAnimationWrapper = new RecentsAnimationWrapper();
-
-    private final long mTouchTimeMs;
-    private long mLauncherFrameDrawnTime;
-
-    private boolean mBgLongSwipeMode = false;
-    private boolean mUiLongSwipeMode = false;
-    private float mLongSwipeDisplacement = 0;
-    private LongSwipeHelper mLongSwipeController;
-
-    private Bundle mAssistData;
-
-    WindowTransformSwipeHandler(int id, RunningTaskInfo runningTaskInfo, Context context,
-            long touchTimeMs, ActivityControlHelper<T> controller) {
-        this.id = id;
-        mContext = context;
-        mRunningTaskInfo = runningTaskInfo;
-        mRunningTaskId = runningTaskInfo.id;
-        mTouchTimeMs = touchTimeMs;
-        mActivityControlHelper = controller;
-        mActivityInitListener = mActivityControlHelper
-                .createActivityInitListener(this::onActivityInit);
-
-        initStateCallbacks();
-        // Register the input consumer on the UI thread, to ensure that it runs after any pending
-        // unregister calls
-        executeOnUiThread(mInputConsumer::registerInputConsumer);
-    }
-
-    private void initStateCallbacks() {
-        mStateCallback = new MultiStateCallback() {
-            @Override
-            public void setState(int stateFlag) {
-                debugNewState(stateFlag);
-                super.setState(stateFlag);
-            }
-        };
-
-        mStateCallback.addCallback(STATE_LAUNCHER_DRAWN | STATE_GESTURE_STARTED_QUICKSCRUB,
-                this::initializeLauncherAnimationController);
-        mStateCallback.addCallback(STATE_LAUNCHER_DRAWN | STATE_GESTURE_STARTED_QUICKSTEP,
-                this::initializeLauncherAnimationController);
-
-        mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN,
-                this::launcherFrameDrawn);
-
-        mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_GESTURE_STARTED_QUICKSTEP,
-                this::notifyGestureStartedAsync);
-        mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_GESTURE_STARTED_QUICKSCRUB,
-                this::notifyGestureStartedAsync);
-
-        mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_STARTED
-                        | STATE_GESTURE_CANCELLED,
-                this::resetStateForAnimationCancel);
-
-        mStateCallback.addCallback(STATE_LAUNCHER_STARTED | STATE_APP_CONTROLLER_RECEIVED,
-                this::sendRemoteAnimationsToAnimationFactory);
-
-        mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_SCALED_CONTROLLER_APP,
-                this::resumeLastTaskForQuickstep);
-        mStateCallback.addCallback(STATE_RESUME_LAST_TASK | STATE_APP_CONTROLLER_RECEIVED,
-                this::resumeLastTask);
-
-        mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
-                        | STATE_ACTIVITY_MULTIPLIER_COMPLETE
-                        | STATE_CAPTURE_SCREENSHOT,
-                this::switchToScreenshot);
-
-        mStateCallback.addCallback(STATE_SCREENSHOT_CAPTURED | STATE_GESTURE_COMPLETED
-                        | STATE_SCALED_CONTROLLER_RECENTS,
-                this::finishCurrentTransitionToHome);
-
-        mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
-                        | STATE_ACTIVITY_MULTIPLIER_COMPLETE | STATE_SCALED_CONTROLLER_RECENTS
-                        | STATE_CURRENT_TASK_FINISHED | STATE_GESTURE_COMPLETED
-                        | STATE_GESTURE_STARTED_QUICKSTEP,
-                this::setupLauncherUiAfterSwipeUpAnimation);
-        mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
-                        | STATE_ACTIVITY_MULTIPLIER_COMPLETE | STATE_SCALED_CONTROLLER_RECENTS
-                        | STATE_CURRENT_TASK_FINISHED | STATE_GESTURE_COMPLETED
-                        | STATE_GESTURE_STARTED_QUICKSTEP | STATE_ASSIST_DATA_RECEIVED,
-                this::preloadAssistData);
-
-        mStateCallback.addCallback(STATE_HANDLER_INVALIDATED, this::invalidateHandler);
-        mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
-                this::invalidateHandlerWithLauncher);
-        mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED
-                | STATE_SCALED_CONTROLLER_APP,
-                this::notifyTransitionCancelled);
-
-        mStateCallback.addCallback(STATE_LAUNCHER_STARTED | STATE_QUICK_SCRUB_START
-                        | STATE_APP_CONTROLLER_RECEIVED, this::onQuickScrubStart);
-        mStateCallback.addCallback(STATE_LAUNCHER_STARTED | STATE_QUICK_SCRUB_START
-                | STATE_SCALED_CONTROLLER_RECENTS, this::onFinishedTransitionToQuickScrub);
-        mStateCallback.addCallback(STATE_LAUNCHER_STARTED | STATE_CURRENT_TASK_FINISHED
-                | STATE_QUICK_SCRUB_END, this::switchToFinalAppAfterQuickScrub);
-
-        mStateCallback.addCallback(LONG_SWIPE_ENTER_STATE, this::checkLongSwipeCanEnter);
-        mStateCallback.addCallback(LONG_SWIPE_START_STATE, this::checkLongSwipeCanStart);
-    }
-
-    private void executeOnUiThread(Runnable action) {
-        if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
-            action.run();
-        } else {
-            postAsyncCallback(mMainThreadHandler, action);
-        }
-    }
-
-    private void setStateOnUiThread(int stateFlag) {
-        if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
-            mStateCallback.setState(stateFlag);
-        } else {
-            postAsyncCallback(mMainThreadHandler, () -> mStateCallback.setState(stateFlag));
-        }
-    }
-
-    private void initTransitionEndpoints(DeviceProfile dp) {
-        mDp = dp;
-
-        TransformedRect tempRect = new TransformedRect();
-        mTransitionDragLength = mActivityControlHelper
-                .getSwipeUpDestinationAndLength(dp, mContext, mInteractionType, tempRect);
-        mClipAnimationHelper.updateTargetRect(tempRect);
-    }
-
-    private long getFadeInDuration() {
-        if (mCurrentShift.getCurrentAnimation() != null) {
-            ObjectAnimator anim = mCurrentShift.getCurrentAnimation();
-            long theirDuration = anim.getDuration() - anim.getCurrentPlayTime();
-
-            // TODO: Find a better heuristic
-            return Math.min(MAX_SWIPE_DURATION, Math.max(theirDuration, MIN_SWIPE_DURATION));
-        } else {
-            return MAX_SWIPE_DURATION;
-        }
-    }
-
-    public void initWhenReady() {
-        mActivityInitListener.register();
-    }
-
-    private boolean onActivityInit(final T activity, Boolean alreadyOnHome) {
-        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);
-            mLayoutListener.setHandler(null);
-        }
-        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();
-        mSyncTransactionApplier = new SyncRtSurfaceTransactionApplier(mRecentsView);
-        mQuickScrubController = mRecentsView.getQuickScrubController();
-        mLayoutListener = mActivityControlHelper.createLayoutListener(mActivity);
-
-        mStateCallback.setState(STATE_LAUNCHER_PRESENT);
-        if (alreadyOnHome) {
-            onLauncherStart(activity);
-        } else {
-            activity.setOnStartCallback(this::onLauncherStart);
-        }
-        return true;
-    }
-
-    private void onLauncherStart(final T activity) {
-        if (mActivity != activity) {
-            return;
-        }
-        if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
-            return;
-        }
-
-        mAnimationFactory = mActivityControlHelper.prepareRecentsUI(mActivity,
-                mWasLauncherAlreadyVisible, this::onAnimatorPlaybackControllerCreated);
-        AbstractFloatingView.closeAllOpenViews(activity, mWasLauncherAlreadyVisible);
-
-        if (mWasLauncherAlreadyVisible) {
-            mStateCallback.setState(STATE_ACTIVITY_MULTIPLIER_COMPLETE | STATE_LAUNCHER_DRAWN);
-        } else {
-            TraceHelper.beginSection("WTS-init");
-            View dragLayer = activity.getDragLayer();
-            mActivityControlHelper.getAlphaProperty(activity).setValue(0);
-            dragLayer.getViewTreeObserver().addOnDrawListener(new OnDrawListener() {
-
-                @Override
-                public void onDraw() {
-                    TraceHelper.endSection("WTS-init", "Launcher frame is drawn");
-                    dragLayer.post(() ->
-                            dragLayer.getViewTreeObserver().removeOnDrawListener(this));
-                    if (activity != mActivity) {
-                        return;
-                    }
-
-                    mStateCallback.setState(STATE_LAUNCHER_DRAWN);
-                }
-            });
-        }
-
-        mRecentsView.showTask(mRunningTaskId);
-        mRecentsView.setRunningTaskHidden(true);
-        mRecentsView.setRunningTaskIconScaledDown(true /* isScaledDown */, false /* animate */);
-        mLayoutListener.open();
-        mStateCallback.setState(STATE_LAUNCHER_STARTED);
-    }
-
-    public void setLauncherOnDrawCallback(Runnable callback) {
-        mLauncherDrawnCallback = callback;
-    }
-
-    private void launcherFrameDrawn() {
-        AlphaProperty property = mActivityControlHelper.getAlphaProperty(mActivity);
-        if (property.getValue() < 1) {
-            if (mGestureStarted) {
-                final MultiStateCallback callback = mStateCallback;
-                ObjectAnimator animator = ObjectAnimator.ofFloat(
-                        property, MultiValueAlpha.VALUE, 1);
-                animator.setDuration(getFadeInDuration()).addListener(
-                        new AnimatorListenerAdapter() {
-                            @Override
-                            public void onAnimationEnd(Animator animation) {
-                                callback.setState(STATE_ACTIVITY_MULTIPLIER_COMPLETE);
-                            }
-                        });
-                animator.start();
-            } else {
-                property.setValue(1);
-                mStateCallback.setState(STATE_ACTIVITY_MULTIPLIER_COMPLETE);
-            }
-        }
-        if (mLauncherDrawnCallback != null) {
-            mLauncherDrawnCallback.run();
-        }
-        mLauncherFrameDrawnTime = SystemClock.uptimeMillis();
-    }
-
-    private void sendRemoteAnimationsToAnimationFactory() {
-        mAnimationFactory.onRemoteAnimationReceived(mRecentsAnimationWrapper.targetSet);
-    }
-
-    private void initializeLauncherAnimationController() {
-        mLayoutListener.setHandler(this);
-        buildAnimationController();
-
-        if (LatencyTrackerCompat.isEnabled(mContext)) {
-            LatencyTrackerCompat.logToggleRecents((int) (mLauncherFrameDrawnTime - mTouchTimeMs));
-        }
-
-        // This method is only called when STATE_GESTURE_STARTED_QUICKSTEP/
-        // STATE_GESTURE_STARTED_QUICKSCRUB 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.getInstance(mContext).getRecentsTaskLoader()
-                .getHighResThumbnailLoader().setVisible(true);
-    }
-
-    public void updateInteractionType(@InteractionType int interactionType) {
-        if (mInteractionType != INTERACTION_NORMAL) {
-            throw new IllegalArgumentException(
-                    "Can't change interaction type from " + mInteractionType);
-        }
-        if (interactionType != INTERACTION_QUICK_SCRUB) {
-            throw new IllegalArgumentException(
-                    "Can't change interaction type to " + interactionType);
-        }
-        mInteractionType = interactionType;
-        mRecentsAnimationWrapper.runOnInit(this::shiftAnimationDestinationForQuickscrub);
-
-        setStateOnUiThread(STATE_QUICK_SCRUB_START | STATE_GESTURE_COMPLETED);
-
-        // Start the window animation without waiting for launcher.
-        animateToProgress(mCurrentShift.value, 1f, QUICK_SCRUB_FROM_APP_START_DURATION, LINEAR,
-                true /* goingToHome */);
-    }
-
-    private void shiftAnimationDestinationForQuickscrub() {
-        TransformedRect tempRect = new TransformedRect();
-        mActivityControlHelper
-                .getSwipeUpDestinationAndLength(mDp, mContext, mInteractionType, tempRect);
-        mClipAnimationHelper.updateTargetRect(tempRect);
-
-        float offsetY =
-                mActivityControlHelper.getTranslationYForQuickScrub(tempRect, mDp, mContext);
-        float scale, offsetX;
-        Resources res = mContext.getResources();
-
-        if (ActivityManagerWrapper.getInstance().getRecentTasks(2, UserHandle.myUserId()).size()
-                < 2) {
-            // There are not enough tasks, we don't need to shift
-            offsetX = 0;
-            scale = 1;
-        } else {
-            offsetX = res.getDimensionPixelSize(R.dimen.recents_page_spacing)
-                    + tempRect.rect.width();
-            float distanceToReachEdge = mDp.widthPx / 2 + tempRect.rect.width() / 2 +
-                    res.getDimensionPixelSize(R.dimen.recents_page_spacing);
-            float interpolation = Math.min(1, offsetX / distanceToReachEdge);
-            scale = TaskView.getCurveScaleForInterpolation(interpolation);
-        }
-        mClipAnimationHelper.offsetTarget(scale, Utilities.isRtl(res) ? -offsetX : offsetX, offsetY,
-                QuickScrubController.QUICK_SCRUB_START_INTERPOLATOR);
-    }
-
-    @WorkerThread
-    public void updateDisplacement(float displacement) {
-        // We are moving in the negative x/y direction
-        displacement = -displacement;
-        if (displacement > mTransitionDragLength) {
-            mCurrentShift.updateValue(1);
-
-            if (!mBgLongSwipeMode) {
-                mBgLongSwipeMode = true;
-                executeOnUiThread(this::onLongSwipeEnabledUi);
-            }
-            mLongSwipeDisplacement = displacement - mTransitionDragLength;
-            executeOnUiThread(this::onLongSwipeDisplacementUpdated);
-        } else {
-            if (mBgLongSwipeMode) {
-                mBgLongSwipeMode = false;
-                executeOnUiThread(this::onLongSwipeDisabledUi);
-            }
-            float translation = Math.max(displacement, 0);
-            float shift = mTransitionDragLength == 0 ? 0 : translation / mTransitionDragLength;
-            mCurrentShift.updateValue(shift);
-        }
-    }
-
-    /**
-     * Called by {@link #mLayoutListener} when launcher layout changes
-     */
-    public void buildAnimationController() {
-        initTransitionEndpoints(mActivity.getDeviceProfile());
-        mAnimationFactory.createActivityController(mTransitionDragLength, mInteractionType);
-    }
-
-    private void onAnimatorPlaybackControllerCreated(AnimatorPlaybackController anim) {
-        mLauncherTransitionController = anim;
-        mLauncherTransitionController.dispatchOnStart();
-        mLauncherTransitionController.setPlayFraction(mCurrentShift.value);
-    }
-
-    @WorkerThread
-    private void updateFinalShift() {
-        float shift = mCurrentShift.value;
-
-        RecentsAnimationControllerCompat controller = mRecentsAnimationWrapper.getController();
-        if (controller != null) {
-
-            mClipAnimationHelper.applyTransform(mRecentsAnimationWrapper.targetSet, shift,
-                    Looper.myLooper() == mMainThreadHandler.getLooper()
-                            ? mSyncTransactionApplier
-                            : null);
-
-            boolean passedThreshold = shift > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD;
-            mRecentsAnimationWrapper.setAnimationTargetsBehindSystemBars(!passedThreshold);
-            if (mActivityControlHelper.shouldMinimizeSplitScreen()) {
-                mRecentsAnimationWrapper.setSplitScreenMinimizedForTransaction(passedThreshold);
-            }
-        }
-
-        executeOnUiThread(this::updateFinalShiftUi);
-    }
-
-    private void updateFinalShiftUi() {
-        final boolean passed = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW;
-        if (passed != mPassedOverviewThreshold) {
-            mPassedOverviewThreshold = passed;
-            if (mInteractionType == INTERACTION_NORMAL && mRecentsView != null) {
-                mRecentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
-                    HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
-            }
-        }
-
-        if (mLauncherTransitionController == null || mLauncherTransitionController
-                .getAnimationPlayer().isStarted()) {
-            return;
-        }
-        mLauncherTransitionController.setPlayFraction(mCurrentShift.value);
-    }
-
-    public void onRecentsAnimationStart(RecentsAnimationControllerCompat controller,
-            RemoteAnimationTargetSet targets, Rect homeContentInsets, Rect minimizedHomeBounds) {
-        LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
-        InvariantDeviceProfile idp = appState == null ?
-                new InvariantDeviceProfile(mContext) : appState.getInvariantDeviceProfile();
-        DeviceProfile dp = idp.getDeviceProfile(mContext);
-        final Rect overviewStackBounds;
-        RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(mRunningTaskId);
-
-        if (minimizedHomeBounds != null && runningTaskTarget != null) {
-            overviewStackBounds = mActivityControlHelper
-                    .getOverviewWindowBounds(minimizedHomeBounds, runningTaskTarget);
-            dp = dp.getMultiWindowProfile(mContext,
-                    new Point(minimizedHomeBounds.width(), minimizedHomeBounds.height()));
-            dp.updateInsets(homeContentInsets);
-        } else {
-            if (mActivity != null) {
-                int loc[] = new int[2];
-                View rootView = mActivity.getRootView();
-                rootView.getLocationOnScreen(loc);
-                overviewStackBounds = new Rect(loc[0], loc[1], loc[0] + rootView.getWidth(),
-                        loc[1] + rootView.getHeight());
-            } else {
-                overviewStackBounds = new Rect(0, 0, dp.widthPx, dp.heightPx);
-            }
-            // If we are not in multi-window mode, home insets should be same as system insets.
-            Rect insets = new Rect();
-            WindowManagerWrapper.getInstance().getStableInsets(insets);
-            dp = dp.copy(mContext);
-            dp.updateInsets(insets);
-        }
-        dp.updateIsSeascape(mContext.getSystemService(WindowManager.class));
-
-        if (runningTaskTarget != null) {
-            mClipAnimationHelper.updateSource(overviewStackBounds, runningTaskTarget);
-        }
-        mClipAnimationHelper.prepareAnimation(false /* isOpening */);
-        initTransitionEndpoints(dp);
-
-        mRecentsAnimationWrapper.setController(controller, targets);
-        setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
-
-        mPassedOverviewThreshold = false;
-    }
-
-    public void onRecentsAnimationCanceled() {
-        mRecentsAnimationWrapper.setController(null, null);
-        mActivityInitListener.unregister();
-        setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED);
-    }
-
-    public void onGestureStarted() {
-        notifyGestureStartedAsync();
-        setStateOnUiThread(mInteractionType == INTERACTION_NORMAL
-                ? STATE_GESTURE_STARTED_QUICKSTEP : STATE_GESTURE_STARTED_QUICKSCRUB);
-        mGestureStarted = true;
-        mRecentsAnimationWrapper.hideCurrentInputMethod();
-        mRecentsAnimationWrapper.enableInputConsumer();
-    }
-
-    /**
-     * Notifies the launcher that the swipe gesture has started. This can be called multiple times
-     * on both background and UI threads
-     */
-    @AnyThread
-    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);
-        }
-    }
-
-    @WorkerThread
-    public void onGestureEnded(float endVelocity) {
-        float flingThreshold = mContext.getResources()
-                .getDimension(R.dimen.quickstep_fling_threshold_velocity);
-        boolean isFling = mGestureStarted && Math.abs(endVelocity) > flingThreshold;
-        setStateOnUiThread(STATE_GESTURE_COMPLETED);
-
-        mLogAction = isFling ? Touch.FLING : Touch.SWIPE;
-
-        if (mBgLongSwipeMode) {
-            executeOnUiThread(() -> onLongSwipeGestureFinishUi(endVelocity, isFling));
-        } else {
-            handleNormalGestureEnd(endVelocity, isFling);
-        }
-    }
-
-    private void handleNormalGestureEnd(float endVelocity, boolean isFling) {
-        float velocityPxPerMs = endVelocity / 1000;
-        long duration = MAX_SWIPE_DURATION;
-        float currentShift = mCurrentShift.value;
-        final boolean goingToHome;
-        float endShift;
-        final float startShift;
-        Interpolator interpolator = DEACCEL;
-        if (!isFling) {
-            goingToHome = currentShift >= MIN_PROGRESS_FOR_OVERVIEW && mGestureStarted;
-            endShift = goingToHome ? 1 : 0;
-            long expectedDuration = Math.abs(Math.round((endShift - currentShift)
-                    * MAX_SWIPE_DURATION * SWIPE_DURATION_MULTIPLIER));
-            duration = Math.min(MAX_SWIPE_DURATION, expectedDuration);
-            startShift = currentShift;
-            interpolator = goingToHome ? OVERSHOOT_1_2 : DEACCEL;
-        } else {
-            goingToHome = endVelocity < 0;
-            endShift = goingToHome ? 1 : 0;
-            startShift = Utilities.boundToRange(currentShift - velocityPxPerMs
-                    * SINGLE_FRAME_MS / mTransitionDragLength, 0, 1);
-            float minFlingVelocity = mContext.getResources()
-                    .getDimension(R.dimen.quickstep_fling_min_velocity);
-            if (Math.abs(endVelocity) > minFlingVelocity && mTransitionDragLength > 0) {
-                if (goingToHome) {
-                    Interpolators.OvershootParams overshoot = new Interpolators.OvershootParams(
-                            startShift, endShift, endShift, velocityPxPerMs, mTransitionDragLength);
-                    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));
-                    duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration);
-                }
-            }
-        }
-        animateToProgress(startShift, endShift, duration, interpolator, goingToHome);
-    }
-
-    private void doLogGesture(boolean toLauncher) {
-        DeviceProfile dp = mDp;
-        if (dp == null) {
-            // We probably never received an animation controller, skip logging.
-            return;
-        }
-        final int direction;
-        if (dp.isVerticalBarLayout()) {
-            direction = (dp.isSeascape() ^ toLauncher) ? Direction.LEFT : Direction.RIGHT;
-        } else {
-            direction = toLauncher ? Direction.UP : Direction.DOWN;
-        }
-
-        int dstContainerType = toLauncher ? ContainerType.TASKSWITCHER : ContainerType.APP;
-        UserEventDispatcher.newInstance(mContext, dp).logStateChangeAction(
-                mLogAction, direction,
-                ContainerType.NAVBAR, ContainerType.APP,
-                dstContainerType,
-                0);
-    }
-
-    /** Animates to the given progress, where 0 is the current app and 1 is overview. */
-    private void animateToProgress(float start, float end, long duration,
-            Interpolator interpolator, boolean goingToHome) {
-        mRecentsAnimationWrapper.runOnInit(() -> animateToProgressInternal(start, end, duration,
-                interpolator, goingToHome));
-    }
-
-    private void animateToProgressInternal(float start, float end, long duration,
-            Interpolator interpolator, boolean goingToHome) {
-        mIsGoingToHome = goingToHome;
-        ObjectAnimator anim = mCurrentShift.animateToValue(start, end).setDuration(duration);
-        anim.setInterpolator(interpolator);
-        anim.addListener(new AnimationSuccessListener() {
-            @Override
-            public void onAnimationSuccess(Animator animator) {
-                setStateOnUiThread(mIsGoingToHome
-                        ? (STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT)
-                        : STATE_SCALED_CONTROLLER_APP);
-            }
-        });
-        anim.start();
-        long startMillis = SystemClock.uptimeMillis();
-        executeOnUiThread(() -> {
-            // Animate the launcher components at the same time as the window, always on UI thread.
-            if (mLauncherTransitionController != null && !mWasLauncherAlreadyVisible
-                    && start != end && duration > 0) {
-                // Adjust start progress and duration in case we are on a different thread.
-                long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
-                elapsedMillis = Utilities.boundToRange(elapsedMillis, 0, duration);
-                float elapsedProgress = (float) elapsedMillis / duration;
-                float adjustedStart = Utilities.mapRange(elapsedProgress, start, end);
-                long adjustedDuration = duration - elapsedMillis;
-                // We want to use the same interpolator as the window, but need to adjust it to
-                // interpolate over the remaining progress (end - start).
-                mLauncherTransitionController.dispatchSetInterpolator(Interpolators.mapToProgress(
-                        interpolator, adjustedStart, end));
-                mLauncherTransitionController.getAnimationPlayer().setDuration(adjustedDuration);
-                mLauncherTransitionController.getAnimationPlayer().start();
-            }
-        });
-    }
-
-    @UiThread
-    private void resumeLastTaskForQuickstep() {
-        setStateOnUiThread(STATE_RESUME_LAST_TASK);
-        doLogGesture(false /* toLauncher */);
-        reset();
-    }
-
-    @UiThread
-    private void resumeLastTask() {
-        mRecentsAnimationWrapper.finish(false /* toHome */, null);
-    }
-
-    public void reset() {
-        if (mInteractionType != INTERACTION_QUICK_SCRUB) {
-            // Only invalidate the handler if we are not quick scrubbing, otherwise, it will be
-            // invalidated after the quick scrub ends
-            setStateOnUiThread(STATE_HANDLER_INVALIDATED);
-        }
-    }
-
-    private void invalidateHandler() {
-        mCurrentShift.finishAnimation();
-
-        if (mGestureEndCallback != null) {
-            mGestureEndCallback.run();
-        }
-
-        mActivityInitListener.unregister();
-        mInputConsumer.unregisterInputConsumer();
-        mTaskSnapshot = null;
-    }
-
-    private void invalidateHandlerWithLauncher() {
-        mLauncherTransitionController = null;
-        mLayoutListener.finish();
-        mActivityControlHelper.getAlphaProperty(mActivity).setValue(1);
-
-        mRecentsView.setRunningTaskHidden(false);
-        mRecentsView.setRunningTaskIconScaledDown(false /* isScaledDown */, false /* animate */);
-        mQuickScrubController.cancelActiveQuickscrub();
-    }
-
-    private void notifyTransitionCancelled() {
-        mAnimationFactory.onTransitionCancelled();
-    }
-
-    private void resetStateForAnimationCancel() {
-        boolean wasVisible = mWasLauncherAlreadyVisible || mGestureStarted;
-        mActivityControlHelper.onTransitionCancelled(mActivity, wasVisible);
-
-        // Leave the pending invisible flag, as it may be used by wallpaper open animation.
-        mActivity.clearForceInvisibleFlag(INVISIBLE_BY_STATE_HANDLER);
-    }
-
-    public void layoutListenerClosed() {
-        if (mWasLauncherAlreadyVisible && mLauncherTransitionController != null) {
-            mLauncherTransitionController.setPlayFraction(1);
-        }
-    }
-
-    private void switchToScreenshot() {
-        boolean finishTransitionPosted = false;
-        RecentsAnimationControllerCompat controller = mRecentsAnimationWrapper.getController();
-        if (controller != null) {
-            // Update the screenshot of the task
-            if (mTaskSnapshot == null) {
-                mTaskSnapshot = controller.screenshotTask(mRunningTaskId);
-            }
-            TaskView taskView = mRecentsView.updateThumbnail(mRunningTaskId, mTaskSnapshot);
-            mRecentsView.setRunningTaskHidden(false);
-            if (taskView != null) {
-                // Defer finishing the animation until the next launcher frame with the
-                // new thumbnail
-                finishTransitionPosted = new WindowCallbacksCompat(taskView) {
-
-                    // The number of frames to defer until we actually finish the animation
-                    private int mDeferFrameCount = 2;
-
-                    @Override
-                    public void onPostDraw(Canvas canvas) {
-                        if (mDeferFrameCount > 0) {
-                            mDeferFrameCount--;
-                            // Workaround, detach and reattach to invalidate the root node for
-                            // another draw
-                            detach();
-                            attach();
-                            taskView.invalidate();
-                            return;
-                        }
-
-                        setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
-                        detach();
-                    }
-                }.attach();
-            }
-        }
-        if (!finishTransitionPosted) {
-            // If we haven't posted a draw callback, set the state immediately.
-            setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
-        }
-    }
-
-    private void finishCurrentTransitionToHome() {
-        synchronized (mRecentsAnimationWrapper) {
-            mRecentsAnimationWrapper.finish(true /* toHome */,
-                    () -> setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
-        }
-    }
-
-    private void setupLauncherUiAfterSwipeUpAnimation() {
-        if (mLauncherTransitionController != null) {
-            mLauncherTransitionController.getAnimationPlayer().end();
-            mLauncherTransitionController = null;
-        }
-        mActivityControlHelper.onSwipeUpComplete(mActivity);
-
-        // Animate the first icon.
-        mRecentsView.setRunningTaskIconScaledDown(false /* isScaledDown */, true /* animate */);
-        mRecentsView.setSwipeDownShouldLaunchApp(true);
-
-        RecentsModel.getInstance(mContext).onOverviewShown(false, TAG);
-
-        doLogGesture(true /* toLauncher */);
-        reset();
-    }
-
-    private void onQuickScrubStart() {
-        if (!mQuickScrubController.prepareQuickScrub(TAG)) {
-            mQuickScrubBlocked = true;
-            setStateOnUiThread(STATE_RESUME_LAST_TASK | STATE_HANDLER_INVALIDATED);
-            return;
-        }
-        if (mLauncherTransitionController != null) {
-            mLauncherTransitionController.getAnimationPlayer().end();
-            mLauncherTransitionController = null;
-        }
-
-        mActivityControlHelper.onQuickInteractionStart(mActivity, mRunningTaskInfo, false);
-
-        // Inform the last progress in case we skipped before.
-        mQuickScrubController.onQuickScrubProgress(mCurrentQuickScrubProgress);
-    }
-
-    private void onFinishedTransitionToQuickScrub() {
-        if (mQuickScrubBlocked) {
-            return;
-        }
-        mQuickScrubController.onFinishedTransitionToQuickScrub();
-
-        mRecentsView.setRunningTaskIconScaledDown(false /* isScaledDown */, true /* animate */);
-        RecentsModel.getInstance(mContext).onOverviewShown(false, TAG);
-    }
-
-    public void onQuickScrubProgress(float progress) {
-        mCurrentQuickScrubProgress = progress;
-        if (Looper.myLooper() != Looper.getMainLooper() || mQuickScrubController == null
-                || mQuickScrubBlocked) {
-            return;
-        }
-        mQuickScrubController.onQuickScrubProgress(progress);
-    }
-
-    public void onQuickScrubEnd() {
-        setStateOnUiThread(STATE_QUICK_SCRUB_END);
-    }
-
-    private void switchToFinalAppAfterQuickScrub() {
-        if (mQuickScrubBlocked) {
-            return;
-        }
-        mQuickScrubController.onQuickScrubEnd();
-
-        // Normally this is handled in reset(), but since we are still scrubbing after the
-        // transition into recents, we need to defer the handler invalidation for quick scrub until
-        // after the gesture ends
-        setStateOnUiThread(STATE_HANDLER_INVALIDATED);
-    }
-
-    private void debugNewState(int stateFlag) {
-        if (!DEBUG_STATES) {
-            return;
-        }
-
-        int state = mStateCallback.getState();
-        StringJoiner currentStateStr = new StringJoiner(", ", "[", "]");
-        String stateFlagStr = "Unknown-" + stateFlag;
-        for (int i = 0; i < STATES.length; i++) {
-            if ((state & (i << i)) != 0) {
-                currentStateStr.add(STATES[i]);
-            }
-            if (stateFlag == (1 << i)) {
-                stateFlagStr = STATES[i] + " (" + stateFlag + ")";
-            }
-        }
-        Log.d(TAG, "[" + System.identityHashCode(this) + "] Adding " + stateFlagStr + " to "
-                + currentStateStr);
-    }
-
-    public void setGestureEndCallback(Runnable gestureEndCallback) {
-        mGestureEndCallback = gestureEndCallback;
-    }
-
-    // Handling long swipe
-    private void onLongSwipeEnabledUi() {
-        mUiLongSwipeMode = true;
-        checkLongSwipeCanEnter();
-        checkLongSwipeCanStart();
-    }
-
-    private void onLongSwipeDisabledUi() {
-        mUiLongSwipeMode = false;
-
-        if (mLongSwipeController != null) {
-            mLongSwipeController.destroy();
-            setTargetAlphaProvider((t, a1) -> a1);
-
-            // Rebuild animations
-            buildAnimationController();
-        }
-    }
-
-    private void onLongSwipeDisplacementUpdated() {
-        if (!mUiLongSwipeMode || mLongSwipeController == null) {
-            return;
-        }
-
-        mLongSwipeController.onMove(mLongSwipeDisplacement);
-    }
-
-    private void checkLongSwipeCanEnter() {
-        if (!mUiLongSwipeMode || !mStateCallback.hasStates(LONG_SWIPE_ENTER_STATE)
-                || !mActivityControlHelper.supportsLongSwipe(mActivity)) {
-            return;
-        }
-
-        // We are entering long swipe mode, make sure the screen shot is captured.
-        mStateCallback.setState(STATE_CAPTURE_SCREENSHOT);
-
-    }
-
-    private void checkLongSwipeCanStart() {
-        if (!mUiLongSwipeMode || !mStateCallback.hasStates(LONG_SWIPE_START_STATE)
-                || !mActivityControlHelper.supportsLongSwipe(mActivity)) {
-            return;
-        }
-
-        RemoteAnimationTargetSet targetSet = mRecentsAnimationWrapper.targetSet;
-        if (targetSet == null) {
-            // This can happen when cancelAnimation comes on the background thread, while we are
-            // processing the long swipe on the UI thread.
-            return;
-        }
-
-        mLongSwipeController = mActivityControlHelper.getLongSwipeController(
-                mActivity, mRecentsAnimationWrapper.targetSet);
-        onLongSwipeDisplacementUpdated();
-        setTargetAlphaProvider(mLongSwipeController::getTargetAlpha);
-    }
-
-    private void onLongSwipeGestureFinishUi(float velocity, boolean isFling) {
-        if (!mUiLongSwipeMode || mLongSwipeController == null) {
-            mUiLongSwipeMode = false;
-            handleNormalGestureEnd(velocity, isFling);
-            return;
-        }
-        mUiLongSwipeMode = false;
-        finishCurrentTransitionToHome();
-        mLongSwipeController.end(velocity, isFling,
-                () -> setStateOnUiThread(STATE_HANDLER_INVALIDATED));
-
-    }
-
-    private void setTargetAlphaProvider(
-            BiFunction<RemoteAnimationTargetCompat, Float, Float> provider) {
-        mClipAnimationHelper.setTaskAlphaCallback(provider);
-        updateFinalShift();
-    }
-
-    public void onAssistDataReceived(Bundle assistData) {
-        mAssistData = assistData;
-        setStateOnUiThread(STATE_ASSIST_DATA_RECEIVED);
-    }
-
-    private void preloadAssistData() {
-        RecentsModel.getInstance(mContext).preloadAssistData(mRunningTaskId, mAssistData);
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
deleted file mode 100644
index 261f45d..0000000
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ /dev/null
@@ -1,74 +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.fallback;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.View;
-
-import com.android.launcher3.DeviceProfile;
-import com.android.quickstep.RecentsActivity;
-import com.android.quickstep.util.LayoutUtils;
-import com.android.quickstep.views.RecentsView;
-
-public class FallbackRecentsView extends RecentsView<RecentsActivity> {
-
-    public FallbackRecentsView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public FallbackRecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        setOverviewStateEnabled(true);
-        getQuickScrubController().onFinishedTransitionToQuickScrub();
-    }
-
-    @Override
-    protected void startHome() {
-        mActivity.startHome();
-    }
-
-    @Override
-    public void onViewAdded(View child) {
-        super.onViewAdded(child);
-        updateEmptyMessage();
-    }
-
-    @Override
-    public void onViewRemoved(View child) {
-        super.onViewRemoved(child);
-        updateEmptyMessage();
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        maybeDrawEmptyMessage(canvas);
-        super.draw(canvas);
-    }
-
-    @Override
-    protected void getTaskSize(DeviceProfile dp, Rect outRect) {
-        LayoutUtils.calculateFallbackTaskSize(getContext(), dp, outRect);
-    }
-
-    @Override
-    public boolean shouldUseMultiWindowTaskSizeStrategy() {
-        // Just use the activity task size for multi-window as well.
-        return false;
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsRootView.java b/quickstep/src/com/android/quickstep/fallback/RecentsRootView.java
deleted file mode 100644
index ca8c252..0000000
--- a/quickstep/src/com/android/quickstep/fallback/RecentsRootView.java
+++ /dev/null
@@ -1,90 +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.fallback;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-
-import com.android.launcher3.BaseActivity;
-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;
-
-public class RecentsRootView extends BaseDragLayer<RecentsActivity> {
-
-    private final RecentsActivity mActivity;
-
-    private final Point mLastKnownSize = new Point(10, 10);
-
-    public RecentsRootView(Context context, AttributeSet attrs) {
-        super(context, attrs, 1 /* alphaChannelCount */);
-        mActivity = (RecentsActivity) BaseActivity.fromContext(context);
-        setSystemUiVisibility(SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
-                | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
-                | SYSTEM_UI_FLAG_LAYOUT_STABLE);
-    }
-
-    public Point getLastKnownSize() {
-        return mLastKnownSize;
-    }
-
-    public void setup() {
-        mControllers = new TouchController[] { new RecentsTaskController(mActivity) };
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        // Check size changes before the actual measure, to avoid multiple measure calls.
-        int width = MeasureSpec.getSize(widthMeasureSpec);
-        int height = MeasureSpec.getSize(heightMeasureSpec);
-        if (mLastKnownSize.x != width || mLastKnownSize.y != height) {
-            mLastKnownSize.set(width, height);
-            mActivity.onRootViewSizeChanged();
-        }
-
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-    }
-
-    @TargetApi(23)
-    @Override
-    protected boolean fitSystemWindows(Rect insets) {
-        // Update device profile before notifying the children.
-        mActivity.getDeviceProfile().updateInsets(insets);
-        setInsets(insets);
-        return true; // I'll take it from here
-    }
-
-    @Override
-    public void setInsets(Rect insets) {
-        // If the insets haven't changed, this is a no-op. Avoid unnecessary layout caused by
-        // modifying child layout params.
-        if (!insets.equals(mInsets)) {
-            super.setInsets(insets);
-        }
-        setBackground(insets.top == 0 ? null
-                : Themes.getAttrDrawable(getContext(), R.attr.workspaceStatusBarScrim));
-    }
-
-    public void dispatchInsets() {
-        mActivity.getDeviceProfile().updateInsets(mInsets);
-        super.setInsets(mInsets);
-    }
-}
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java b/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java
deleted file mode 100644
index 9463cc9..0000000
--- a/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java
+++ /dev/null
@@ -1,31 +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.fallback;
-
-import com.android.launcher3.uioverrides.TaskViewTouchController;
-import com.android.quickstep.RecentsActivity;
-
-public class RecentsTaskController extends TaskViewTouchController<RecentsActivity> {
-
-    public RecentsTaskController(RecentsActivity activity) {
-        super(activity);
-    }
-
-    @Override
-    protected boolean isRecentsInteractive() {
-        return mActivity.hasWindowFocus();
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
new file mode 100644
index 0000000..2f411ef
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -0,0 +1,105 @@
+/*
+ * 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.content.Intent;
+import android.stats.launcher.nano.LauncherExtension;
+import android.stats.launcher.nano.LauncherTarget;
+
+import static android.stats.launcher.nano.Launcher.ALLAPPS;
+import static android.stats.launcher.nano.Launcher.HOME;
+import static android.stats.launcher.nano.Launcher.LAUNCH_APP;
+import static android.stats.launcher.nano.Launcher.LAUNCH_TASK;
+import static android.stats.launcher.nano.Launcher.BACKGROUND;
+import static android.stats.launcher.nano.Launcher.OVERVIEW;
+
+import android.view.View;
+
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.logging.StatsLogUtils;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+import com.android.launcher3.util.ComponentKey;
+import com.android.systemui.shared.system.StatsLogCompat;
+import com.google.protobuf.nano.MessageNano;
+
+import androidx.annotation.Nullable;
+
+/**
+ * This method calls the StatsLog hidden method until they are made available public.
+ *
+ * To see if the logs are properly sent to statsd, execute following command.
+ * $ adb root && adb shell statsd
+ * $ adb shell cmd stats print-logs
+ * $ adb logcat | grep statsd  OR $ adb logcat -b stats
+ */
+public class StatsLogCompatManager extends StatsLogManager {
+
+    private static final int SUPPORTED_TARGET_DEPTH = 2;
+
+    public StatsLogCompatManager(Context context) { }
+
+    @Override
+    public void logAppLaunch(View v, Intent intent) {
+        LauncherExtension ext = new LauncherExtension();
+        ext.srcTarget = new LauncherTarget[SUPPORTED_TARGET_DEPTH];
+        int srcState = mStateProvider.getCurrentState();
+        fillInLauncherExtension(v, ext);
+        StatsLogCompat.write(LAUNCH_APP, srcState, BACKGROUND /* dstState */,
+                MessageNano.toByteArray(ext), true);
+    }
+
+    @Override
+    public void logTaskLaunch(View v, ComponentKey componentKey) {
+        LauncherExtension ext = new LauncherExtension();
+        ext.srcTarget = new LauncherTarget[SUPPORTED_TARGET_DEPTH];
+        int srcState = OVERVIEW;
+        fillInLauncherExtension(v, ext);
+        StatsLogCompat.write(LAUNCH_TASK, srcState, BACKGROUND /* dstState */,
+                MessageNano.toByteArray(ext), true);
+    }
+
+    public static boolean fillInLauncherExtension(View v, LauncherExtension extension) {
+        StatsLogUtils.LogContainerProvider provider = StatsLogUtils.getLaunchProviderRecursive(v);
+        if (v == null || !(v.getTag() instanceof ItemInfo) || provider == null) {
+            return false;
+        }
+        ItemInfo itemInfo = (ItemInfo) v.getTag();
+        Target child = new Target();
+        Target parent = new Target();
+        provider.fillInLogContainerData(v, itemInfo, child, parent);
+        copy(child, extension.srcTarget[0]);
+        copy(parent, extension.srcTarget[1]);
+        return true;
+    }
+
+    private static void copy(Target src, LauncherTarget dst) {
+        // fill in
+    }
+
+    @Override
+    public void verify() {
+        if(!(StatsLogUtils.LAUNCHER_STATE_ALLAPPS == ALLAPPS &&
+                StatsLogUtils.LAUNCHER_STATE_BACKGROUND == BACKGROUND &&
+                StatsLogUtils.LAUNCHER_STATE_OVERVIEW == OVERVIEW &&
+                StatsLogUtils.LAUNCHER_STATE_HOME == HOME)) {
+            throw new IllegalStateException(
+                    "StatsLogUtil constants doesn't match enums in launcher.proto");
+        }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java b/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java
index 6dff187..4a11601 100644
--- a/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java
+++ b/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java
@@ -36,17 +36,19 @@
 @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 srcChildTargetType,
-                                     int srcParentContainerType, int dstContainerType,
-                                     int pageIndex) {
+    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, srcChildTargetType, srcParentContainerType,
-                dstContainerType, pageIndex);
+        super.logStateChangeAction(action, dir, downX, downY, srcChildTargetType,
+                srcParentContainerType, dstContainerType, pageIndex);
     }
 
     public void logActionTip(int actionType, int viewType) {
diff --git a/quickstep/src/com/android/quickstep/util/BinderTracker.java b/quickstep/src/com/android/quickstep/util/BinderTracker.java
new file mode 100644
index 0000000..32d0d53
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/BinderTracker.java
@@ -0,0 +1,63 @@
+/*
+ * 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 android.os.Binder;
+import android.os.IBinder;
+import android.os.Looper;
+import android.util.Log;
+
+import com.android.launcher3.config.FeatureFlags;
+
+/**
+ * Utility class to test and check binder calls during development.
+ */
+public class BinderTracker {
+
+    private static final String TAG = "BinderTracker";
+
+    public static void start() {
+        if (!FeatureFlags.IS_DOGFOOD_BUILD) {
+            Log.wtf(TAG, "Accessing tracker in released code.", new Exception());
+            return;
+        }
+
+        Binder.setProxyTransactListener(new Tracker());
+    }
+
+    public static void stop() {
+        if (!FeatureFlags.IS_DOGFOOD_BUILD) {
+            Log.wtf(TAG, "Accessing tracker in released code.", new Exception());
+            return;
+        }
+        Binder.setProxyTransactListener(null);
+    }
+
+    private static class Tracker implements Binder.ProxyTransactListener {
+
+        @Override
+        public Object onTransactStarted(IBinder iBinder, int code) {
+            if (Looper.myLooper() == Looper.getMainLooper()) {
+                Log.e(TAG, "Binder call on ui thread", new Exception());
+            }
+            return null;
+        }
+
+        @Override
+        public void onTransactEnded(Object session) { }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/util/CachedEventDispatcher.java b/quickstep/src/com/android/quickstep/util/CachedEventDispatcher.java
new file mode 100644
index 0000000..194c7d4
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/CachedEventDispatcher.java
@@ -0,0 +1,69 @@
+/*
+ * 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.systemui.shared.system.InputChannelCompat.mergeMotionEvent;
+
+import android.view.MotionEvent;
+
+import java.util.ArrayList;
+import java.util.function.Consumer;
+
+/**
+ * Utility class to dispatch touch events to a different class. It stores the events locally
+ * until a valid dispatcher is available.
+ */
+public class CachedEventDispatcher {
+
+    private Consumer<MotionEvent> mConsumer;
+
+    private ArrayList<MotionEvent> mCache;
+    private MotionEvent mLastEvent;
+
+    public void dispatchEvent(MotionEvent event) {
+        if (mConsumer != null) {
+            mConsumer.accept(event);
+        } else {
+            if (mLastEvent == null || !mergeMotionEvent(event, mLastEvent)) {
+                // Queue event.
+                if (mCache == null) {
+                    mCache = new ArrayList<>();
+                }
+                mLastEvent = MotionEvent.obtain(event);
+                mCache.add(mLastEvent);
+            }
+        }
+    }
+
+    public void setConsumer(Consumer<MotionEvent> consumer) {
+        if (consumer == null) {
+            return;
+        }
+        mConsumer = consumer;
+        int cacheCount = mCache == null ? 0 : mCache.size();
+        for (int i = 0; i < cacheCount; i++) {
+            MotionEvent ev = mCache.get(i);
+            mConsumer.accept(ev);
+            ev.recycle();
+        }
+        mCache = null;
+        mLastEvent = null;
+    }
+
+    public boolean hasConsumer() {
+        return mConsumer != null;
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java b/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
deleted file mode 100644
index df70a8a..0000000
--- a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
+++ /dev/null
@@ -1,322 +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.util;
-
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_TRANSLATION_Y_FACTOR;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
-
-import android.annotation.TargetApi;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Matrix.ScaleToFit;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.os.Build;
-import android.os.RemoteException;
-import android.support.annotation.Nullable;
-import android.view.Surface;
-import android.view.animation.Interpolator;
-
-import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.views.BaseDragLayer;
-import com.android.quickstep.RecentsModel;
-import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskThumbnailView;
-import com.android.systemui.shared.recents.ISystemUiProxy;
-import com.android.systemui.shared.recents.utilities.RectFEvaluator;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier.SurfaceParams;
-import com.android.systemui.shared.system.TransactionCompat;
-import com.android.systemui.shared.system.WindowManagerWrapper;
-
-import java.util.function.BiConsumer;
-import java.util.function.BiFunction;
-
-/**
- * Utility class to handle window clip animation
- */
-@TargetApi(Build.VERSION_CODES.P)
-public class ClipAnimationHelper {
-
-    // The bounds of the source app in device coordinates
-    private final Rect mSourceStackBounds = new Rect();
-    // The insets of the source app
-    private final Rect mSourceInsets = new Rect();
-    // The source app bounds with the source insets applied, in the source app window coordinates
-    private final RectF mSourceRect = new RectF();
-    // The bounds of the task view in launcher window coordinates
-    private final RectF mTargetRect = new RectF();
-    // Set when the final window destination is changed, such as offsetting for quick scrub
-    private final PointF mTargetOffset = new PointF();
-    // The insets to be used for clipping the app window, which can be larger than mSourceInsets
-    // if the aspect ratio of the target is smaller than the aspect ratio of the source rect. In
-    // app window coordinates.
-    private final RectF mSourceWindowClipInsets = new RectF();
-
-    // The bounds of launcher (not including insets) in device coordinates
-    public final Rect mHomeStackBounds = new Rect();
-
-    // The clip rect in source app window coordinates
-    private final Rect mClipRect = new Rect();
-    private final RectFEvaluator mRectFEvaluator = new RectFEvaluator();
-    private final Matrix mTmpMatrix = new Matrix();
-    private final RectF mTmpRectF = new RectF();
-
-    private float mTargetScale = 1f;
-    private float mOffsetScale = 1f;
-    private Interpolator mInterpolator = LINEAR;
-    // We translate y slightly faster than the rest of the animation for quick scrub.
-    private Interpolator mOffsetYInterpolator = LINEAR;
-
-    // Whether to boost the opening animation target layers, or the closing
-    private int mBoostModeTargetLayers = -1;
-    // Wether or not applyTransform has been called yet since prepareAnimation()
-    private boolean mIsFirstFrame = true;
-
-    private BiFunction<RemoteAnimationTargetCompat, Float, Float> mTaskAlphaCallback =
-            (t, a1) -> a1;
-
-    private void updateSourceStack(RemoteAnimationTargetCompat target) {
-        mSourceInsets.set(target.contentInsets);
-        mSourceStackBounds.set(target.sourceContainerBounds);
-
-        // TODO: Should sourceContainerBounds already have this offset?
-        mSourceStackBounds.offsetTo(target.position.x, target.position.y);
-
-    }
-
-    public void updateSource(Rect homeStackBounds, RemoteAnimationTargetCompat target) {
-        mHomeStackBounds.set(homeStackBounds);
-        updateSourceStack(target);
-    }
-
-    public void updateTargetRect(TransformedRect targetRect) {
-        mOffsetScale = targetRect.scale;
-        mSourceRect.set(mSourceInsets.left, mSourceInsets.top,
-                mSourceStackBounds.width() - mSourceInsets.right,
-                mSourceStackBounds.height() - mSourceInsets.bottom);
-        mTargetRect.set(targetRect.rect);
-        Utilities.scaleRectFAboutCenter(mTargetRect, targetRect.scale);
-        mTargetRect.offset(mHomeStackBounds.left - mSourceStackBounds.left,
-                mHomeStackBounds.top - mSourceStackBounds.top);
-
-        // Calculate the clip based on the target rect (since the content insets and the
-        // launcher insets may differ, so the aspect ratio of the target rect can differ
-        // from the source rect. The difference between the target rect (scaled to the
-        // source rect) is the amount to clip on each edge.
-        RectF scaledTargetRect = new RectF(mTargetRect);
-        Utilities.scaleRectFAboutCenter(scaledTargetRect,
-                mSourceRect.width() / mTargetRect.width());
-        scaledTargetRect.offsetTo(mSourceRect.left, mSourceRect.top);
-        mSourceWindowClipInsets.set(
-                Math.max(scaledTargetRect.left, 0),
-                Math.max(scaledTargetRect.top, 0),
-                Math.max(mSourceStackBounds.width() - scaledTargetRect.right, 0),
-                Math.max(mSourceStackBounds.height() - scaledTargetRect.bottom, 0));
-        mSourceRect.set(scaledTargetRect);
-    }
-
-    public void prepareAnimation(boolean isOpening) {
-        mBoostModeTargetLayers = isOpening ? MODE_OPENING : MODE_CLOSING;
-    }
-
-    public RectF applyTransform(RemoteAnimationTargetSet targetSet, float progress,
-            @Nullable SyncRtSurfaceTransactionApplier syncTransactionApplier) {
-        RectF currentRect;
-        mTmpRectF.set(mTargetRect);
-        Utilities.scaleRectFAboutCenter(mTmpRectF, mTargetScale);
-        float offsetYProgress = mOffsetYInterpolator.getInterpolation(progress);
-        progress = mInterpolator.getInterpolation(progress);
-        currentRect =  mRectFEvaluator.evaluate(progress, mSourceRect, mTmpRectF);
-
-        synchronized (mTargetOffset) {
-            // Stay lined up with the center of the target, since it moves for quick scrub.
-            currentRect.offset(mTargetOffset.x * mOffsetScale * progress,
-                    mTargetOffset.y  * offsetYProgress);
-        }
-
-        mClipRect.left = (int) (mSourceWindowClipInsets.left * progress);
-        mClipRect.top = (int) (mSourceWindowClipInsets.top * progress);
-        mClipRect.right = (int)
-                (mSourceStackBounds.width() - (mSourceWindowClipInsets.right * progress));
-        mClipRect.bottom = (int)
-                (mSourceStackBounds.height() - (mSourceWindowClipInsets.bottom * progress));
-
-        SurfaceParams[] params = new SurfaceParams[targetSet.unfilteredApps.length];
-        for (int i = 0; i < targetSet.unfilteredApps.length; i++) {
-            RemoteAnimationTargetCompat app = targetSet.unfilteredApps[i];
-            mTmpMatrix.setTranslate(app.position.x, app.position.y);
-            Rect crop = app.sourceContainerBounds;
-            float alpha = 1f;
-            if (app.mode == targetSet.targetMode) {
-                if (app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
-                    mTmpMatrix.setRectToRect(mSourceRect, currentRect, ScaleToFit.FILL);
-                    mTmpMatrix.postTranslate(app.position.x, app.position.y);
-                    crop = mClipRect;
-                }
-
-                if (app.isNotInRecents
-                        || app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
-                    alpha = 1 - progress;
-                }
-
-                alpha = mTaskAlphaCallback.apply(app, alpha);
-            }
-
-            params[i] = new SurfaceParams(app.leash, alpha, mTmpMatrix, crop,
-                    RemoteAnimationProvider.getLayer(app, mBoostModeTargetLayers));
-        }
-        applyParams(syncTransactionApplier, params);
-        return currentRect;
-    }
-
-    private void applyParams(@Nullable SyncRtSurfaceTransactionApplier syncTransactionApplier,
-            SurfaceParams[] params) {
-        if (syncTransactionApplier != null) {
-            syncTransactionApplier.scheduleApply(params);
-        } else {
-            TransactionCompat t = new TransactionCompat();
-            for (SurfaceParams param : params) {
-                SyncRtSurfaceTransactionApplier.applyParams(t, param);
-            }
-            t.setEarlyWakeup();
-            t.apply();
-        }
-    }
-
-    public void setTaskAlphaCallback(
-            BiFunction<RemoteAnimationTargetCompat, Float, Float> callback) {
-        mTaskAlphaCallback = callback;
-    }
-
-    public void offsetTarget(float scale, float offsetX, float offsetY, Interpolator interpolator) {
-        synchronized (mTargetOffset) {
-            mTargetOffset.set(offsetX, offsetY);
-        }
-        mTargetScale = scale;
-        mInterpolator = interpolator;
-        mOffsetYInterpolator = Interpolators.clampToProgress(mInterpolator, 0,
-                QUICK_SCRUB_TRANSLATION_Y_FACTOR);
-    }
-
-    public void fromTaskThumbnailView(TaskThumbnailView ttv, RecentsView rv) {
-        fromTaskThumbnailView(ttv, rv, null);
-    }
-
-    public void fromTaskThumbnailView(TaskThumbnailView ttv, RecentsView rv,
-            @Nullable RemoteAnimationTargetCompat target) {
-        BaseDraggingActivity activity = BaseDraggingActivity.fromContext(ttv.getContext());
-        BaseDragLayer dl = activity.getDragLayer();
-
-        int[] pos = new int[2];
-        dl.getLocationOnScreen(pos);
-        mHomeStackBounds.set(0, 0, dl.getWidth(), dl.getHeight());
-        mHomeStackBounds.offset(pos[0], pos[1]);
-
-        if (target != null) {
-            updateSourceStack(target);
-        } else  if (rv.shouldUseMultiWindowTaskSizeStrategy()) {
-            updateStackBoundsToMultiWindowTaskSize(activity);
-        } else {
-            mSourceStackBounds.set(mHomeStackBounds);
-            mSourceInsets.set(activity.getDeviceProfile().getInsets());
-        }
-
-        TransformedRect targetRect = new TransformedRect();
-        dl.getDescendantRectRelativeToSelf(ttv, targetRect.rect);
-        updateTargetRect(targetRect);
-
-        if (target == null) {
-            // Transform the clip relative to the target rect. Only do this in the case where we
-            // aren't applying the insets to the app windows (where the clip should be in target app
-            // space)
-            float scale = mTargetRect.width() / mSourceRect.width();
-            mSourceWindowClipInsets.left = mSourceWindowClipInsets.left * scale;
-            mSourceWindowClipInsets.top = mSourceWindowClipInsets.top * scale;
-            mSourceWindowClipInsets.right = mSourceWindowClipInsets.right * scale;
-            mSourceWindowClipInsets.bottom = mSourceWindowClipInsets.bottom * scale;
-        }
-    }
-
-    private void updateStackBoundsToMultiWindowTaskSize(BaseDraggingActivity activity) {
-        ISystemUiProxy sysUiProxy = RecentsModel.getInstance(activity).getSystemUiProxy();
-        if (sysUiProxy != null) {
-            try {
-                mSourceStackBounds.set(sysUiProxy.getNonMinimizedSplitScreenSecondaryBounds());
-                return;
-            } catch (RemoteException e) {
-                // Use half screen size
-            }
-        }
-
-        // Assume that the task size is half screen size (minus the insets and the divider size)
-        DeviceProfile fullDp = activity.getDeviceProfile().getFullScreenProfile();
-        // Use availableWidthPx and availableHeightPx instead of widthPx and heightPx to
-        // account for system insets
-        int taskWidth = fullDp.availableWidthPx;
-        int taskHeight = fullDp.availableHeightPx;
-        int halfDividerSize = activity.getResources()
-                .getDimensionPixelSize(R.dimen.multi_window_task_divider_size) / 2;
-
-        Rect insets = new Rect();
-        WindowManagerWrapper.getInstance().getStableInsets(insets);
-        if (fullDp.isLandscape) {
-            taskWidth = taskWidth / 2 - halfDividerSize;
-        } else {
-            taskHeight = taskHeight / 2 - halfDividerSize;
-        }
-
-        // Align the task to bottom left/right edge (closer to nav bar).
-        int left = activity.getDeviceProfile().isSeascape() ? insets.left
-                : (insets.left + fullDp.availableWidthPx - taskWidth);
-        mSourceStackBounds.set(0, 0, taskWidth, taskHeight);
-        mSourceStackBounds.offset(left, insets.top + fullDp.availableHeightPx - taskHeight);
-    }
-
-    public void drawForProgress(TaskThumbnailView ttv, Canvas canvas, float progress) {
-        RectF currentRect =  mRectFEvaluator.evaluate(progress, mSourceRect, mTargetRect);
-        canvas.translate(mSourceStackBounds.left - mHomeStackBounds.left,
-                mSourceStackBounds.top - mHomeStackBounds.top);
-        mTmpMatrix.setRectToRect(mTargetRect, currentRect, ScaleToFit.FILL);
-
-        canvas.concat(mTmpMatrix);
-        canvas.translate(mTargetRect.left, mTargetRect.top);
-
-        float insetProgress = (1 - progress);
-        ttv.drawOnCanvas(canvas,
-                -mSourceWindowClipInsets.left * insetProgress,
-                -mSourceWindowClipInsets.top * insetProgress,
-                ttv.getMeasuredWidth() + mSourceWindowClipInsets.right * insetProgress,
-                ttv.getMeasuredHeight() + mSourceWindowClipInsets.bottom * insetProgress,
-                ttv.getCornerRadius() * progress);
-    }
-
-    public RectF getTargetRect() {
-        return mTargetRect;
-    }
-
-    public RectF getSourceRect() {
-        return mSourceRect;
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index ec9c7ea..050bdff 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -20,11 +20,13 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Rect;
-import android.support.annotation.AnyThread;
-import android.support.annotation.IntDef;
+
+import androidx.annotation.AnyThread;
+import androidx.annotation.IntDef;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
+import com.android.launcher3.config.FeatureFlags;
 
 import java.lang.annotation.Retention;
 
@@ -108,6 +110,14 @@
         float y = insets.top + Math.max(topIconMargin,
                 (launcherVisibleHeight - extraVerticalSpace - outHeight) / 2);
         outRect.set(Math.round(x), Math.round(y),
-                Math.round(x + outWidth), Math.round(y + outHeight));
+                Math.round(x) + Math.round(outWidth), Math.round(y) + Math.round(outHeight));
+    }
+
+    public static int getShelfTrackingDistance(Context context, DeviceProfile dp) {
+        // Track the bottom of the window.
+        int shelfHeight = dp.hotseatBarSizePx + dp.getInsets().bottom;
+        int spaceBetweenShelfAndRecents = (int) context.getResources().getDimension(
+                R.dimen.task_card_vert_space);
+        return shelfHeight + spaceBetweenShelfAndRecents;
     }
 }
diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
new file mode 100644
index 0000000..801a560
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
@@ -0,0 +1,198 @@
+/*
+ * 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 android.content.Context;
+import android.content.res.Resources;
+import android.view.MotionEvent;
+
+import com.android.launcher3.Alarm;
+import com.android.launcher3.R;
+import com.android.launcher3.compat.AccessibilityManagerCompat;
+
+/**
+ * Given positions along x- or y-axis, tracks velocity and acceleration and determines when there is
+ * a pause in motion.
+ */
+public class MotionPauseDetector {
+
+    // The percentage of the previous speed that determines whether this is a rapid deceleration.
+    // The bigger this number, the easier it is to trigger the first pause.
+    private static final float RAPID_DECELERATION_FACTOR = 0.6f;
+
+    /** If no motion is added for this amount of time, assume the motion has paused. */
+    private static final long FORCE_PAUSE_TIMEOUT = 300;
+
+    /**
+     * After {@link #makePauseHarderToTrigger()}, must
+     * move slowly for this long to trigger a pause.
+     */
+    private static final long HARDER_TRIGGER_TIMEOUT = 400;
+
+    private final float mSpeedVerySlow;
+    private final float mSpeedSlow;
+    private final float mSpeedSomewhatFast;
+    private final float mSpeedFast;
+    private final Alarm mForcePauseTimeout;
+    private final boolean mMakePauseHarderToTrigger;
+    private final Context mContext;
+
+    private Long mPreviousTime = null;
+    private Float mPreviousPosition = null;
+    private Float mPreviousVelocity = null;
+
+    private Float mFirstPosition = null;
+
+    private OnMotionPauseListener mOnMotionPauseListener;
+    private boolean mIsPaused;
+    // Bias more for the first pause to make it feel extra responsive.
+    private boolean mHasEverBeenPaused;
+    /** @see #setDisallowPause(boolean) */
+    private boolean mDisallowPause;
+    // Time at which speed became < mSpeedSlow (only used if mMakePauseHarderToTrigger == true).
+    private long mSlowStartTime;
+
+    public MotionPauseDetector(Context context) {
+        this(context, false);
+    }
+
+    /**
+     * @param makePauseHarderToTrigger Used for gestures that require a more explicit pause.
+     */
+    public MotionPauseDetector(Context context, boolean makePauseHarderToTrigger) {
+        mContext = context;
+        Resources res = context.getResources();
+        mSpeedVerySlow = res.getDimension(R.dimen.motion_pause_detector_speed_very_slow);
+        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);
+        mForcePauseTimeout = new Alarm();
+        mForcePauseTimeout.setOnAlarmListener(alarm -> updatePaused(true /* isPaused */));
+        mMakePauseHarderToTrigger = makePauseHarderToTrigger;
+    }
+
+    /**
+     * Get callbacks for when motion pauses and resumes.
+     */
+    public void setOnMotionPauseListener(OnMotionPauseListener listener) {
+        mOnMotionPauseListener = listener;
+    }
+
+    /**
+     * @param disallowPause If true, we will not detect any pauses until this is set to false again.
+     */
+    public void setDisallowPause(boolean disallowPause) {
+        mDisallowPause = disallowPause;
+        updatePaused(mIsPaused);
+    }
+
+    /**
+     * Computes velocity and acceleration to determine whether the motion is paused.
+     * @param position The x or y component of the motion being tracked.
+     *
+     * TODO: Use historical positions as well, e.g. {@link MotionEvent#getHistoricalY(int, int)}.
+     */
+    public void addPosition(float position, long time) {
+        if (mFirstPosition == null) {
+            mFirstPosition = position;
+        }
+        mForcePauseTimeout.setAlarm(mMakePauseHarderToTrigger
+                ? HARDER_TRIGGER_TIMEOUT
+                : FORCE_PAUSE_TIMEOUT);
+        if (mPreviousTime != null && mPreviousPosition != null) {
+            long changeInTime = Math.max(1, time - mPreviousTime);
+            float changeInPosition = position - mPreviousPosition;
+            float velocity = changeInPosition / changeInTime;
+            if (mPreviousVelocity != null) {
+                checkMotionPaused(velocity, mPreviousVelocity, time);
+            }
+            mPreviousVelocity = velocity;
+        }
+        mPreviousTime = time;
+        mPreviousPosition = position;
+    }
+
+    private void checkMotionPaused(float velocity, float prevVelocity, long time) {
+        float speed = Math.abs(velocity);
+        float previousSpeed = Math.abs(prevVelocity);
+        boolean isPaused;
+        if (mIsPaused) {
+            // Continue to be paused until moving at a fast speed.
+            isPaused = speed < mSpeedFast || previousSpeed < mSpeedFast;
+        } else {
+            if (velocity < 0 != prevVelocity < 0) {
+                // We're just changing directions, not necessarily stopping.
+                isPaused = false;
+            } else {
+                isPaused = speed < mSpeedVerySlow && previousSpeed < mSpeedVerySlow;
+                if (!isPaused && !mHasEverBeenPaused) {
+                    // We want to be more aggressive about detecting the first pause to ensure it
+                    // feels as responsive as possible; getting two very slow speeds back to back
+                    // takes too long, so also check for a rapid deceleration.
+                    boolean isRapidDeceleration = speed < previousSpeed * RAPID_DECELERATION_FACTOR;
+                    isPaused = isRapidDeceleration && speed < mSpeedSomewhatFast;
+                }
+                if (mMakePauseHarderToTrigger) {
+                    if (speed < mSpeedSlow) {
+                        if (mSlowStartTime == 0) {
+                            mSlowStartTime = time;
+                        }
+                        isPaused = time - mSlowStartTime >= HARDER_TRIGGER_TIMEOUT;
+                    } else {
+                        mSlowStartTime = 0;
+                        isPaused = false;
+                    }
+                }
+            }
+        }
+        updatePaused(isPaused);
+    }
+
+    private void updatePaused(boolean isPaused) {
+        if (mDisallowPause) {
+            isPaused = false;
+        }
+        if (mIsPaused != isPaused) {
+            mIsPaused = isPaused;
+            if (mIsPaused) {
+                AccessibilityManagerCompat.sendPauseDetectedEventToTest(mContext);
+                mHasEverBeenPaused = true;
+            }
+            if (mOnMotionPauseListener != null) {
+                mOnMotionPauseListener.onMotionPauseChanged(mIsPaused);
+            }
+        }
+    }
+
+    public void clear() {
+        mPreviousTime = null;
+        mPreviousPosition = null;
+        mPreviousVelocity = null;
+        mFirstPosition = null;
+        setOnMotionPauseListener(null);
+        mIsPaused = mHasEverBeenPaused = false;
+        mSlowStartTime = 0;
+        mForcePauseTimeout.cancelAlarm();
+    }
+
+    public boolean isPaused() {
+        return mIsPaused;
+    }
+
+    public interface OnMotionPauseListener {
+        void onMotionPauseChanged(boolean isPaused);
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/util/RemoteAnimationTargetSet.java b/quickstep/src/com/android/quickstep/util/RemoteAnimationTargetSet.java
index c372485..1229293 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteAnimationTargetSet.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteAnimationTargetSet.java
@@ -16,31 +16,43 @@
 package com.android.quickstep.util;
 
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
 
+import java.util.ArrayDeque;
 import java.util.ArrayList;
+import java.util.Queue;
 
 /**
  * Holds a collection of RemoteAnimationTargets, filtered by different properties.
  */
 public class RemoteAnimationTargetSet {
 
+    private final Queue<SyncRtSurfaceTransactionApplierCompat> mDependentTransactionAppliers =
+            new ArrayDeque<>(1);
+
     public final RemoteAnimationTargetCompat[] unfilteredApps;
     public final RemoteAnimationTargetCompat[] apps;
     public final int targetMode;
+    public final boolean hasRecents;
 
     public RemoteAnimationTargetSet(RemoteAnimationTargetCompat[] apps, int targetMode) {
         ArrayList<RemoteAnimationTargetCompat> filteredApps = new ArrayList<>();
+        boolean hasRecents = false;
         if (apps != null) {
             for (RemoteAnimationTargetCompat target : apps) {
                 if (target.mode == targetMode) {
                     filteredApps.add(target);
                 }
+
+                hasRecents |= target.activityType ==
+                        RemoteAnimationTargetCompat.ACTIVITY_TYPE_RECENTS;
             }
         }
 
         this.unfilteredApps = apps;
         this.apps = filteredApps.toArray(new RemoteAnimationTargetCompat[filteredApps.size()]);
         this.targetMode = targetMode;
+        this.hasRecents = hasRecents;
     }
 
     public RemoteAnimationTargetCompat findTask(int taskId) {
@@ -60,4 +72,19 @@
         }
         return false;
     }
+
+    public void addDependentTransactionApplier(SyncRtSurfaceTransactionApplierCompat delay) {
+        mDependentTransactionAppliers.add(delay);
+    }
+
+    public void release() {
+        SyncRtSurfaceTransactionApplierCompat applier = mDependentTransactionAppliers.poll();
+        if (applier == null) {
+            for (RemoteAnimationTargetCompat target : unfilteredApps) {
+                target.release();
+            }
+        } else {
+            applier.addAfterApplyCallback(this::release);
+        }
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewDrawable.java b/quickstep/src/com/android/quickstep/util/TaskViewDrawable.java
deleted file mode 100644
index 48b07a7..0000000
--- a/quickstep/src/com/android/quickstep/util/TaskViewDrawable.java
+++ /dev/null
@@ -1,142 +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.util;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.PixelFormat;
-import android.graphics.drawable.Drawable;
-import android.util.FloatProperty;
-import android.view.View;
-
-import com.android.launcher3.Utilities;
-import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskThumbnailView;
-import com.android.quickstep.views.TaskView;
-
-public class TaskViewDrawable extends Drawable {
-
-    public static final FloatProperty<TaskViewDrawable> PROGRESS =
-            new FloatProperty<TaskViewDrawable>("progress") {
-                @Override
-                public void setValue(TaskViewDrawable taskViewDrawable, float v) {
-                    taskViewDrawable.setProgress(v);
-                }
-
-                @Override
-                public Float get(TaskViewDrawable taskViewDrawable) {
-                    return taskViewDrawable.mProgress;
-                }
-            };
-
-    /**
-     * The progress at which we play the atomic icon scale animation.
-     */
-    private static final float ICON_SCALE_THRESHOLD = 0.95f;
-
-    private final RecentsView mParent;
-    private final View mIconView;
-    private final int[] mIconPos;
-
-    private final TaskThumbnailView mThumbnailView;
-
-    private final ClipAnimationHelper mClipAnimationHelper;
-
-    private float mProgress = 1;
-    private boolean mPassedIconScaleThreshold;
-    private ValueAnimator mIconScaleAnimator;
-    private float mIconScale;
-
-    public TaskViewDrawable(TaskView tv, RecentsView parent) {
-        mParent = parent;
-        mIconView = tv.getIconView();
-        mIconPos = new int[2];
-        mIconScale = mIconView.getScaleX();
-        Utilities.getDescendantCoordRelativeToAncestor(mIconView, parent, mIconPos, true);
-
-        mThumbnailView = tv.getThumbnail();
-        mClipAnimationHelper = new ClipAnimationHelper();
-        mClipAnimationHelper.fromTaskThumbnailView(mThumbnailView, parent);
-    }
-
-    public void setProgress(float progress) {
-        mProgress = progress;
-        mParent.invalidate();
-        boolean passedIconScaleThreshold = progress <= ICON_SCALE_THRESHOLD;
-        if (mPassedIconScaleThreshold != passedIconScaleThreshold) {
-            mPassedIconScaleThreshold = passedIconScaleThreshold;
-            animateIconScale(mPassedIconScaleThreshold ? 0 : 1);
-        }
-    }
-
-    private void animateIconScale(float toScale) {
-        if (mIconScaleAnimator != null) {
-            mIconScaleAnimator.cancel();
-        }
-        mIconScaleAnimator = ValueAnimator.ofFloat(mIconScale, toScale);
-        mIconScaleAnimator.addUpdateListener(valueAnimator -> {
-            mIconScale = (float) valueAnimator.getAnimatedValue();
-            if (mProgress > ICON_SCALE_THRESHOLD) {
-                // Speed up the icon scale to ensure it is 1 when progress is 1.
-                float iconProgress = (mProgress - ICON_SCALE_THRESHOLD) / (1 - ICON_SCALE_THRESHOLD);
-                if (iconProgress > mIconScale) {
-                    mIconScale = iconProgress;
-                }
-            }
-            invalidateSelf();
-        });
-        mIconScaleAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mIconScaleAnimator = null;
-            }
-        });
-        mIconScaleAnimator.setDuration(TaskView.SCALE_ICON_DURATION);
-        mIconScaleAnimator.start();
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        canvas.save();
-        canvas.translate(mParent.getScrollX(), mParent.getScrollY());
-        mClipAnimationHelper.drawForProgress(mThumbnailView, canvas, mProgress);
-        canvas.restore();
-
-        canvas.save();
-        canvas.translate(mIconPos[0], mIconPos[1]);
-        canvas.scale(mIconScale, mIconScale, mIconView.getWidth() / 2, mIconView.getHeight() / 2);
-        mIconView.draw(canvas);
-        canvas.restore();
-    }
-
-    public ClipAnimationHelper getClipAnimationHelper() {
-        return mClipAnimationHelper;
-    }
-
-    @Override
-    public void setAlpha(int i) { }
-
-    @Override
-    public void setColorFilter(ColorFilter colorFilter) { }
-
-    @Override
-    public int getOpacity() {
-        return PixelFormat.TRANSLUCENT;
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/util/TransformedRect.java b/quickstep/src/com/android/quickstep/util/TransformedRect.java
deleted file mode 100644
index 79f11e4..0000000
--- a/quickstep/src/com/android/quickstep/util/TransformedRect.java
+++ /dev/null
@@ -1,32 +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.util;
-
-import android.graphics.Rect;
-
-/**
- * A wrapper around {@link Rect} with additional transformation properties
- */
-public class TransformedRect {
-
-    public final Rect rect = new Rect();
-    public float scale = 1;
-
-    public void set(TransformedRect transformedRect) {
-        rect.set(transformedRect.rect);
-        scale = transformedRect.scale;
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
deleted file mode 100644
index fbecd84..0000000
--- a/quickstep/src/com/android/quickstep/views/ClearAllButton.java
+++ /dev/null
@@ -1,79 +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.widget.Button;
-
-import com.android.launcher3.Utilities;
-import com.android.quickstep.views.RecentsView.PageCallbacks;
-import com.android.quickstep.views.RecentsView.ScrollState;
-
-public class ClearAllButton extends Button implements PageCallbacks {
-
-    private float mScrollAlpha = 1;
-    private float mContentAlpha = 1;
-
-    private final boolean mIsRtl;
-
-    private int mScrollOffset;
-
-    public ClearAllButton(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        mIsRtl = Utilities.isRtl(context.getResources());
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-
-        RecentsView parent = (RecentsView) getParent();
-        mScrollOffset = mIsRtl ? parent.getPaddingRight() / 2 : - parent.getPaddingLeft() / 2;
-    }
-
-    @Override
-    public boolean hasOverlappingRendering() {
-        return false;
-    }
-
-    public void setContentAlpha(float alpha) {
-        if (mContentAlpha != alpha) {
-            mContentAlpha = alpha;
-            updateAlpha();
-        }
-    }
-
-    @Override
-    public void onPageScroll(ScrollState scrollState) {
-        float width = getWidth();
-        if (width == 0) {
-            return;
-        }
-
-        float shift = Math.min(scrollState.scrollFromEdge, width);
-        setTranslationX(mIsRtl ? (mScrollOffset - shift) : (mScrollOffset + shift));
-        mScrollAlpha = 1 - shift / width;
-        updateAlpha();
-    }
-
-    private void updateAlpha() {
-        final float alpha = mScrollAlpha * mContentAlpha;
-        setAlpha(alpha);
-        setClickable(alpha == 1);
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/views/IconView.java b/quickstep/src/com/android/quickstep/views/IconView.java
deleted file mode 100644
index c359966..0000000
--- a/quickstep/src/com/android/quickstep/views/IconView.java
+++ /dev/null
@@ -1,91 +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.graphics.Canvas;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.view.View;
-
-/**
- * A view which draws a drawable stretched to fit its size. Unlike ImageView, it avoids relayout
- * when the drawable changes.
- */
-public class IconView extends View {
-
-    private Drawable mDrawable;
-
-    public IconView(Context context) {
-        super(context);
-    }
-
-    public IconView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public IconView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    public void setDrawable(Drawable d) {
-        if (mDrawable != null) {
-            mDrawable.setCallback(null);
-        }
-        mDrawable = d;
-        if (mDrawable != null) {
-            mDrawable.setCallback(this);
-            mDrawable.setBounds(0, 0, getWidth(), getHeight());
-        }
-        invalidate();
-    }
-
-    @Override
-    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-        super.onSizeChanged(w, h, oldw, oldh);
-        if (mDrawable != null) {
-            mDrawable.setBounds(0, 0, w, h);
-        }
-    }
-
-    @Override
-    protected boolean verifyDrawable(Drawable who) {
-        return super.verifyDrawable(who) || who == mDrawable;
-    }
-
-    @Override
-    protected void drawableStateChanged() {
-        super.drawableStateChanged();
-
-        final Drawable drawable = mDrawable;
-        if (drawable != null && drawable.isStateful()
-                && drawable.setState(getDrawableState())) {
-            invalidateDrawable(drawable);
-        }
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        if (mDrawable != null) {
-            mDrawable.draw(canvas);
-        }
-    }
-
-    @Override
-    public boolean hasOverlappingRendering() {
-        return false;
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/views/LauncherLayoutListener.java b/quickstep/src/com/android/quickstep/views/LauncherLayoutListener.java
deleted file mode 100644
index c149de5..0000000
--- a/quickstep/src/com/android/quickstep/views/LauncherLayoutListener.java
+++ /dev/null
@@ -1,108 +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 com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
-import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
-
-import android.graphics.Rect;
-import android.view.MotionEvent;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.Insettable;
-import com.android.launcher3.Launcher;
-import com.android.quickstep.ActivityControlHelper.LayoutListener;
-import com.android.quickstep.WindowTransformSwipeHandler;
-
-/**
- * Floating view which shows the task snapshot allowing it to be dragged and placed.
- */
-public class LauncherLayoutListener extends AbstractFloatingView
-        implements Insettable, LayoutListener {
-
-    private final Launcher mLauncher;
-    private WindowTransformSwipeHandler mHandler;
-
-    public LauncherLayoutListener(Launcher launcher) {
-        super(launcher, null);
-        mLauncher = launcher;
-        setVisibility(INVISIBLE);
-
-        // For the duration of the gesture, lock the screen orientation to ensure that we do not
-        // rotate mid-quickscrub
-        launcher.getRotationHelper().setStateHandlerRequest(REQUEST_LOCK);
-    }
-
-    @Override
-    public void setHandler(WindowTransformSwipeHandler handler) {
-        mHandler = handler;
-    }
-
-    @Override
-    public void setInsets(Rect insets) {
-        if (mHandler != null) {
-            mHandler.buildAnimationController();
-        }
-    }
-
-    @Override
-    public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
-        return false;
-    }
-
-    @Override
-    protected void handleClose(boolean animate) {
-        if (mIsOpen) {
-            mIsOpen = false;
-            // We don't support animate.
-            mLauncher.getDragLayer().removeView(this);
-
-            if (mHandler != null) {
-                mHandler.layoutListenerClosed();
-            }
-        }
-    }
-
-    @Override
-    public void open() {
-        if (!mIsOpen) {
-            mLauncher.getDragLayer().addView(this);
-            mIsOpen = true;
-        }
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        setMeasuredDimension(1, 1);
-    }
-
-    @Override
-    public void logActionCommand(int command) {
-        // We should probably log the weather
-    }
-
-    @Override
-    protected boolean isOfType(int type) {
-        return (type & TYPE_QUICKSTEP_PREVIEW) != 0;
-    }
-
-    @Override
-    public void finish() {
-        setHandler(null);
-        close(false);
-        mLauncher.getRotationHelper().setStateHandlerRequest(REQUEST_NONE);
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
deleted file mode 100644
index 7c5828b..0000000
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ /dev/null
@@ -1,171 +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.LauncherAppTransitionManagerImpl.ALL_APPS_PROGRESS_OFF_SCREEN;
-import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA;
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
-
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-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.util.FloatProperty;
-import android.view.View;
-import android.view.ViewDebug;
-
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.R;
-import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.views.ScrimView;
-import com.android.quickstep.OverviewInteractionState;
-import com.android.quickstep.util.ClipAnimationHelper;
-import com.android.quickstep.util.LayoutUtils;
-
-/**
- * {@link RecentsView} used in Launcher activity
- */
-@TargetApi(Build.VERSION_CODES.O)
-public class LauncherRecentsView extends RecentsView<Launcher> {
-
-    public static final FloatProperty<LauncherRecentsView> TRANSLATION_Y_FACTOR =
-            new FloatProperty<LauncherRecentsView>("translationYFactor") {
-
-                @Override
-                public void setValue(LauncherRecentsView view, float v) {
-                    view.setTranslationYFactor(v);
-                }
-
-                @Override
-                public Float get(LauncherRecentsView view) {
-                    return view.mTranslationYFactor;
-                }
-            };
-
-    @ViewDebug.ExportedProperty(category = "launcher")
-    private float mTranslationYFactor;
-
-    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);
-        setContentAlpha(0);
-    }
-
-    @Override
-    protected void startHome() {
-        mActivity.getStateManager().goToState(NORMAL);
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-        setTranslationYFactor(mTranslationYFactor);
-    }
-
-    public void setTranslationYFactor(float translationFactor) {
-        mTranslationYFactor = translationFactor;
-        setTranslationY(computeTranslationYForFactor(mTranslationYFactor));
-    }
-
-    public float computeTranslationYForFactor(float translationYFactor) {
-        return translationYFactor * (getPaddingBottom() - getPaddingTop());
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        maybeDrawEmptyMessage(canvas);
-        super.draw(canvas);
-    }
-
-    @Override
-    public void onViewAdded(View child) {
-        super.onViewAdded(child);
-        updateEmptyMessage();
-    }
-
-    @Override
-    protected void onTaskStackUpdated() {
-        // Lazily update the empty message only when the task stack is reapplied
-        updateEmptyMessage();
-    }
-
-    /**
-     * Animates adjacent tasks and translate hotseat off screen as well.
-     */
-    @Override
-    public AnimatorSet createAdjacentPageAnimForTaskLaunch(TaskView tv,
-            ClipAnimationHelper helper) {
-        AnimatorSet anim = super.createAdjacentPageAnimForTaskLaunch(tv, helper);
-
-        if (!OverviewInteractionState.getInstance(mActivity).isSwipeUpGestureEnabled()) {
-            // 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.findViewById(R.id.scrim_view), ScrimView.DRAG_HANDLE_ALPHA, 0);
-        dragHandleAnim.setInterpolator(Interpolators.ACCEL_2);
-        anim.play(dragHandleAnim);
-
-        return anim;
-    }
-
-    @Override
-    protected void getTaskSize(DeviceProfile dp, Rect outRect) {
-        LayoutUtils.calculateLauncherTaskSize(getContext(), dp, outRect);
-    }
-
-    @Override
-    protected void onTaskLaunched(boolean success) {
-        if (success) {
-            mActivity.getStateManager().goToState(NORMAL, false /* animate */);
-        } else {
-            LauncherState state = mActivity.getStateManager().getState();
-            mActivity.getAllAppsController().setState(state);
-        }
-        super.onTaskLaunched(success);
-    }
-
-    @Override
-    public boolean shouldUseMultiWindowTaskSizeStrategy() {
-        return mActivity.isInMultiWindowModeCompat();
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
deleted file mode 100644
index e18708b..0000000
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ /dev/null
@@ -1,1367 +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 com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
-import static com.android.launcher3.anim.Interpolators.ACCEL;
-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.util.SystemUiController.UI_STATE_OVERVIEW;
-import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
-import static com.android.quickstep.WindowTransformSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
-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.Canvas;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.support.annotation.Nullable;
-import android.text.Layout;
-import android.text.StaticLayout;
-import android.text.TextPaint;
-import android.util.ArraySet;
-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.ViewConfiguration;
-import android.view.ViewDebug;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.widget.ListView;
-
-import com.android.launcher3.BaseActivity;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Insettable;
-import com.android.launcher3.PagedView;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.anim.PropertyListBuilder;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.launcher3.util.PendingAnimation;
-import com.android.launcher3.util.Themes;
-import com.android.quickstep.OverviewCallbacks;
-import com.android.quickstep.QuickScrubController;
-import com.android.quickstep.RecentsModel;
-import com.android.quickstep.TaskUtils;
-import com.android.quickstep.util.ClipAnimationHelper;
-import com.android.quickstep.util.TaskViewDrawable;
-import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
-import com.android.systemui.shared.recents.model.RecentsTaskLoader;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.TaskStack;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.BackgroundExecutor;
-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.P)
-public abstract class RecentsView<T extends BaseActivity> extends PagedView implements Insettable {
-
-    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();
-                }
-            };
-
-    private final Rect mTempRect = new Rect();
-
-    private static final int DISMISS_TASK_DURATION = 300;
-    // 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;
-
-    private static final float[] sTempFloatArray = new float[3];
-
-    protected final T mActivity;
-    private final QuickScrubController mQuickScrubController;
-    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();
-
-    /**
-     * TODO: Call reloadIdNeeded in onTaskStackChanged.
-     */
-    private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
-        @Override
-        public void onTaskSnapshotChanged(int taskId, ThumbnailData snapshot) {
-            if (!mHandleTaskStackChanges) {
-                return;
-            }
-            updateThumbnail(taskId, snapshot);
-        }
-
-        @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;
-            }
-            // TODO: Re-enable layout transitions for addition of the unpinned task
-            reloadIfNeeded();
-        }
-
-        @Override
-        public void onTaskRemoved(int taskId) {
-            if (!mHandleTaskStackChanges) {
-                return;
-            }
-            BackgroundExecutor.get().submit(() -> {
-                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 {
-                    RecentsTaskLoadPlan loadPlan = new RecentsTaskLoadPlan(getContext());
-                    RecentsTaskLoadPlan.PreloadOptions opts =
-                            new RecentsTaskLoadPlan.PreloadOptions();
-                    opts.loadTitles = false;
-                    loadPlan.preloadPlan(opts, mModel.getRecentsTaskLoader(), -1,
-                            UserHandle.myUserId());
-                    if (loadPlan.getTaskStack().findTaskWithId(taskId) == null) {
-                        // The task was removed from the recents list
-                        handler.post(() ->
-                                dismissTask(taskView, true /* animate */, false /* removeTask */));
-                    }
-                }
-            });
-        }
-
-        @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);
-        }
-    };
-
-    private int mLoadPlanId = -1;
-
-    // Only valid until the launcher state changes to NORMAL
-    private int mRunningTaskId = -1;
-    private boolean mRunningTaskTileHidden;
-    private Task mTmpRunningTask;
-
-    private boolean mRunningTaskIconScaledDown = false;
-
-    private boolean mOverviewStateEnabled;
-    private boolean mHandleTaskStackChanges;
-    private Runnable mNextPageSwitchRunnable;
-    private boolean mSwipeDownShouldLaunchApp;
-    private boolean mTouchDownToStartHome;
-    private final int mTouchSlop;
-    private int mDownX;
-    private int mDownY;
-
-    private PendingAnimation mPendingAnimation;
-
-    @ViewDebug.ExportedProperty(category = "launcher")
-    private float mContentAlpha = 1;
-
-    // Keeps track of task views whose visual state should not be reset
-    private ArraySet<TaskView> mIgnoreResetTaskViews = new ArraySet<>();
-
-    // 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 Layout mEmptyTextLayout;
-
-    private BaseActivity.MultiWindowModeChangedListener mMultiWindowModeChangedListener =
-            (inMultiWindowMode) -> {
-        if (!inMultiWindowMode && mOverviewStateEnabled) {
-            // TODO: Re-enable layout transitions for addition of the unpinned task
-            reloadIfNeeded();
-        }
-    };
-
-    public RecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        setPageSpacing(getResources().getDimensionPixelSize(R.dimen.recents_page_spacing));
-        enableFreeScroll(true);
-
-        mFastFlingVelocity = getResources()
-                .getDimensionPixelSize(R.dimen.recents_fast_fling_velocity);
-        mActivity = (T) BaseActivity.fromContext(context);
-        mQuickScrubController = new QuickScrubController(mActivity, this);
-        mModel = RecentsModel.getInstance(context);
-
-        mClearAllButton = (ClearAllButton) LayoutInflater.from(context)
-                .inflate(R.layout.overview_clear_all_button, this, false);
-        mClearAllButton.setOnClickListener(this::dismissAllTasks);
-
-        mIsRtl = !Utilities.isRtl(getResources());
-        setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
-        mTaskTopMargin = getResources()
-                .getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
-        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
-
-        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));
-        mEmptyMessagePadding = getResources()
-                .getDimensionPixelSize(R.dimen.recents_empty_message_text_padding);
-        setWillNotDraw(false);
-        updateEmptyMessage();
-    }
-
-    public boolean isRtl() {
-        return mIsRtl;
-    }
-
-    public TaskView updateThumbnail(int taskId, ThumbnailData thumbnailData) {
-        TaskView taskView = getTaskView(taskId);
-        if (taskView != null) {
-            taskView.onTaskDataLoaded(taskView.getTask(), thumbnailData);
-        }
-        return taskView;
-    }
-
-    @Override
-    protected void onWindowVisibilityChanged(int visibility) {
-        super.onWindowVisibilityChanged(visibility);
-        updateTaskStackListenerState();
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        updateTaskStackListenerState();
-        mActivity.addMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
-        ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        updateTaskStackListenerState();
-        mActivity.removeMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
-        ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener);
-    }
-
-    @Override
-    public void onViewRemoved(View child) {
-        super.onViewRemoved(child);
-
-        // Clear the task data for the removed child if it was visible
-        if (child != mClearAllButton) {
-            Task task = ((TaskView) child).getTask();
-            if (mHasVisibleTaskData.get(task.key.id)) {
-                mHasVisibleTaskData.delete(task.key.id);
-                RecentsTaskLoader loader = mModel.getRecentsTaskLoader();
-                loader.unloadTaskData(task);
-                loader.getHighResThumbnailLoader().onTaskInvisible(task);
-            }
-        }
-    }
-
-    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 = (TaskView) getChildAt(i);
-            if (tv.getTask().key.id == taskId) {
-                return tv;
-            }
-        }
-        return null;
-    }
-
-    public void setOverviewStateEnabled(boolean enabled) {
-        mOverviewStateEnabled = enabled;
-        updateTaskStackListenerState();
-    }
-
-    public void setNextPageSwitchRunnable(Runnable r) {
-        mNextPageSwitchRunnable = r;
-    }
-
-    @Override
-    protected void onPageEndTransition() {
-        super.onPageEndTransition();
-        if (mNextPageSwitchRunnable != null) {
-            mNextPageSwitchRunnable.run();
-            mNextPageSwitchRunnable = null;
-        }
-        if (getNextPage() > 0) {
-            setSwipeDownShouldLaunchApp(true);
-        }
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        super.onTouchEvent(ev);
-        final int x = (int) ev.getX();
-        final int y = (int) ev.getY();
-        switch (ev.getAction()) {
-            case MotionEvent.ACTION_UP:
-                if (mShowEmptyMessage) {
-                    onAllTasksRemoved();
-                }
-                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 && Math.hypot(mDownX - x, mDownY - y) > mTouchSlop) {
-                    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 (mTouchState == TOUCH_STATE_REST) {
-                    updateDeadZoneRects();
-                    final boolean clearAllButtonDeadZoneConsumed = mClearAllButton.getAlpha() == 1
-                            && mClearAllButtonDeadZoneRect.contains(x, y);
-                    if (!clearAllButtonDeadZoneConsumed
-                            && !mTaskViewDeadZoneRect.contains(x + getScrollX(), y)) {
-                        mTouchDownToStartHome = true;
-                    }
-                }
-                mDownX = x;
-                mDownY = y;
-                break;
-        }
-
-
-        // Do not let touch escape to siblings below this view.
-        return true;
-    }
-
-    private void applyLoadPlan(RecentsTaskLoadPlan loadPlan) {
-        if (mPendingAnimation != null) {
-            mPendingAnimation.addEndListener((onEndListener) -> applyLoadPlan(loadPlan));
-            return;
-        }
-        TaskStack stack = loadPlan != null ? loadPlan.getTaskStack() : null;
-        if (stack == null) {
-            removeAllViews();
-            onTaskStackUpdated();
-            return;
-        }
-
-        int oldChildCount = getChildCount();
-
-        // Ensure there are as many views as there are tasks in the stack (adding and trimming as
-        // necessary)
-        final LayoutInflater inflater = LayoutInflater.from(getContext());
-        final ArrayList<Task> tasks = new ArrayList<>(stack.getTasks());
-
-        final int requiredTaskCount = tasks.size();
-        if (getTaskViewCount() != requiredTaskCount) {
-            if (oldChildCount > 0) {
-                removeView(mClearAllButton);
-            }
-            for (int i = getChildCount(); i < requiredTaskCount; i++) {
-                final TaskView taskView = (TaskView) inflater.inflate(R.layout.task, this, false);
-                addView(taskView);
-            }
-            while (getChildCount() > requiredTaskCount) {
-                final TaskView taskView = (TaskView) getChildAt(getChildCount() - 1);
-                removeView(taskView);
-            }
-            if (requiredTaskCount > 0) {
-                addView(mClearAllButton);
-            }
-        }
-
-        // Unload existing visible task data
-        unloadVisibleTaskData();
-
-        // Rebind and reset all task views
-        for (int i = requiredTaskCount - 1; i >= 0; i--) {
-            final int pageIndex = requiredTaskCount - i - 1;
-            final Task task = tasks.get(i);
-            final TaskView taskView = (TaskView) getChildAt(pageIndex);
-            taskView.bind(task);
-        }
-        resetTaskVisuals();
-
-        if (oldChildCount != getChildCount()) {
-            mQuickScrubController.snapToNextTaskIfAvailable();
-        }
-        onTaskStackUpdated();
-    }
-
-    public int getTaskViewCount() {
-        // Account for the clear all button.
-        int childCount = getChildCount();
-        return childCount == 0 ? 0 : childCount - 1;
-    }
-
-    protected void onTaskStackUpdated() { }
-
-    public void resetTaskVisuals() {
-        for (int i = getTaskViewCount() - 1; i >= 0; i--) {
-            TaskView taskView = (TaskView) getChildAt(i);
-            if (!mIgnoreResetTaskViews.contains(taskView)) {
-                taskView.resetVisualProperties();
-            }
-        }
-        if (mRunningTaskTileHidden) {
-            setRunningTaskHidden(mRunningTaskTileHidden);
-        }
-        applyIconScale(false /* animate */);
-
-        updateCurveProperties();
-        // Update the set of visible task's data
-        loadVisibleTaskData();
-    }
-
-    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);
-        DeviceProfile dp = mActivity.getDeviceProfile();
-        getTaskSize(dp, mTempRect);
-
-        // Keep this logic in sync with ActivityControlHelper.getTranslationYForQuickScrub.
-        mTempRect.top -= mTaskTopMargin;
-        setPadding(mTempRect.left - mInsets.left, mTempRect.top - mInsets.top,
-                dp.availableWidthPx + mInsets.left - mTempRect.right,
-                dp.availableHeightPx + mInsets.top - mTempRect.bottom);
-    }
-
-    protected abstract void getTaskSize(DeviceProfile dp, Rect outRect);
-
-    public void getTaskSize(Rect outRect) {
-        getTaskSize(mActivity.getDeviceProfile(), outRect);
-    }
-
-    @Override
-    protected boolean computeScrollHelper() {
-        boolean scrolling = super.computeScrollHelper();
-        boolean isFlingingFast = false;
-        updateCurveProperties();
-        if (scrolling || (mTouchState == TOUCH_STATE_SCROLLING)) {
-            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
-        RecentsTaskLoader loader = mModel.getRecentsTaskLoader();
-        loader.getHighResThumbnailLoader().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 scrollX = getScrollX();
-        final int halfPageWidth = getNormalChildWidth() / 2;
-        final int screenCenter = mInsets.left + getPaddingLeft() + scrollX + halfPageWidth;
-        final int halfScreenWidth = getMeasuredWidth() / 2;
-        final int pageSpacing = mPageSpacing;
-        mScrollState.scrollFromEdge = mIsRtl ? scrollX : (mMaxScrollX - scrollX);
-
-        final int pageCount = getPageCount();
-        for (int i = 0; i < pageCount; i++) {
-            View page = getPageAt(i);
-            float pageCenter = page.getLeft() + page.getTranslationX() + halfPageWidth;
-            float distanceFromScreenCenter = screenCenter - pageCenter;
-            float distanceToReachEdge = halfScreenWidth + halfPageWidth + pageSpacing;
-            mScrollState.linearInterpolation = Math.min(1,
-                    Math.abs(distanceFromScreenCenter) / distanceToReachEdge);
-            ((PageCallbacks) page).onPageScroll(mScrollState);
-        }
-    }
-
-    /**
-     * Iterates through all thet asks, 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) {
-            // Skip loading visible task data if we've already left the overview state
-            return;
-        }
-
-        RecentsTaskLoader loader = mModel.getRecentsTaskLoader();
-        int centerPageIndex = getPageNearestToCenterOfScreen();
-        int numChildren = getTaskViewCount();
-        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 < numChildren; i++) {
-            TaskView taskView = (TaskView) getChildAt(i);
-            Task task = taskView.getTask();
-            boolean visible = lower <= i && i <= 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)) {
-                    loader.loadTaskData(task);
-                    loader.getHighResThumbnailLoader().onTaskVisible(task);
-                }
-                mHasVisibleTaskData.put(task.key.id, visible);
-            } else {
-                if (mHasVisibleTaskData.get(task.key.id)) {
-                    loader.unloadTaskData(task);
-                    loader.getHighResThumbnailLoader().onTaskInvisible(task);
-                }
-                mHasVisibleTaskData.delete(task.key.id);
-            }
-        }
-    }
-
-    /**
-     * Unloads any associated data from the currently visible tasks
-     */
-    private void unloadVisibleTaskData() {
-        RecentsTaskLoader loader = mModel.getRecentsTaskLoader();
-        for (int i = 0; i < mHasVisibleTaskData.size(); i++) {
-            if (mHasVisibleTaskData.valueAt(i)) {
-                TaskView taskView = getTaskView(mHasVisibleTaskData.keyAt(i));
-                Task task = taskView.getTask();
-                loader.unloadTaskData(task);
-                loader.getHighResThumbnailLoader().onTaskInvisible(task);
-            }
-        }
-        mHasVisibleTaskData.clear();
-    }
-
-    protected void onAllTasksRemoved() {
-        startHome();
-    }
-
-    protected abstract void startHome();
-
-    public void reset() {
-        mRunningTaskId = -1;
-        mRunningTaskTileHidden = false;
-
-        unloadVisibleTaskData();
-        setCurrentPage(0);
-
-        OverviewCallbacks.get(getContext()).onResetOverview();
-    }
-
-    /**
-     * Reloads the view if anything in recents changed.
-     */
-    public void reloadIfNeeded() {
-        if (!mModel.isLoadPlanValid(mLoadPlanId)) {
-            mLoadPlanId = mModel.loadTasks(mRunningTaskId, this::applyLoadPlan);
-        }
-    }
-
-    /**
-     * Ensures that the first task in the view represents {@param task} and reloads the view
-     * if needed. This allows the swipe-up gesture to assume that the first tile always
-     * corresponds to the correct task.
-     * 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 showTask(int runningTaskId) {
-        if (getChildCount() == 0) {
-            // Add an empty view for now until the task plan is loaded and applied
-            final TaskView taskView = (TaskView) LayoutInflater.from(getContext())
-                    .inflate(R.layout.task, this, false);
-            addView(taskView);
-            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 = new Task(new Task.TaskKey(runningTaskId, 0, new Intent(),
-                    new ComponentName(getContext(), getClass()), 0, 0), null, null, "", "", 0, 0,
-                    false, true, false, false, new ActivityManager.TaskDescription(), 0,
-                    new ComponentName("", ""), false);
-            taskView.bind(mTmpRunningTask);
-        }
-        setCurrentTask(runningTaskId);
-    }
-
-    /**
-     * Hides the tile associated with {@link #mRunningTaskId}
-     */
-    public void setRunningTaskHidden(boolean isHidden) {
-        mRunningTaskTileHidden = isHidden;
-        TaskView runningTask = getTaskView(mRunningTaskId);
-        if (runningTask != null) {
-            runningTask.setAlpha(isHidden ? 0 : mContentAlpha);
-        }
-    }
-
-    /**
-     * Similar to {@link #showTask(int)} but does not put any restrictions on the first tile.
-     */
-    public void setCurrentTask(int runningTaskId) {
-        boolean runningTaskTileHidden = mRunningTaskTileHidden;
-        boolean runningTaskIconScaledDown = mRunningTaskIconScaledDown;
-
-        setRunningTaskIconScaledDown(false, false);
-        setRunningTaskHidden(false);
-        mRunningTaskId = runningTaskId;
-        setRunningTaskIconScaledDown(runningTaskIconScaledDown, false);
-        setRunningTaskHidden(runningTaskTileHidden);
-
-        setCurrentPage(0);
-
-        // Load the tasks (if the loading is already
-        mLoadPlanId = mModel.loadTasks(runningTaskId, this::applyLoadPlan);
-    }
-
-    public void showNextTask() {
-        TaskView runningTaskView = getTaskView(mRunningTaskId);
-        if (runningTaskView == null) {
-            // Launch the first task
-            if (getTaskViewCount() > 0) {
-                ((TaskView) getChildAt(0)).launchTask(true /* animate */);
-            }
-        } else {
-            // Get the next launch task
-            int runningTaskIndex = indexOfChild(runningTaskView);
-            int nextTaskIndex = Math.max(0, Math.min(getTaskViewCount() - 1, runningTaskIndex + 1));
-            if (nextTaskIndex < getTaskViewCount()) {
-                ((TaskView) getChildAt(nextTaskIndex)).launchTask(true /* animate */);
-            }
-        }
-    }
-
-    public QuickScrubController getQuickScrubController() {
-        return mQuickScrubController;
-    }
-
-    public void setRunningTaskIconScaledDown(boolean isScaledDown, boolean animate) {
-        if (mRunningTaskIconScaledDown == isScaledDown) {
-            return;
-        }
-        mRunningTaskIconScaledDown = isScaledDown;
-        applyIconScale(animate);
-    }
-
-    private void applyIconScale(boolean animate) {
-        float scale = mRunningTaskIconScaledDown ? 0 : 1;
-        TaskView firstTask = getTaskView(mRunningTaskId);
-        if (firstTask != null) {
-            if (animate) {
-                firstTask.animateIconToScaleAndDim(scale);
-            } else {
-                firstTask.setIconScaleAndDim(scale);
-            }
-        }
-    }
-
-    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 {
-
-        /**
-         * 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;
-    }
-
-    public void addIgnoreResetTask(TaskView taskView) {
-        mIgnoreResetTaskViews.add(taskView);
-    }
-
-    public void removeIgnoreResetTask(TaskView taskView) {
-        mIgnoreResetTaskViews.remove(taskView);
-    }
-
-    private void addDismissedTaskAnimations(View taskView, AnimatorSet anim, long duration) {
-        addAnim(ObjectAnimator.ofFloat(taskView, ALPHA, 0), duration, ACCEL_2, anim);
-        addAnim(ObjectAnimator.ofFloat(taskView, TRANSLATION_Y, -taskView.getHeight()),
-                duration, LINEAR, anim);
-    }
-
-    private void removeTask(Task task, int index, PendingAnimation.OnEndListener onEndListener,
-                            boolean shouldLog) {
-        if (task != null) {
-            ActivityManagerWrapper.getInstance().removeTask(task.key.id);
-            if (shouldLog) {
-                mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
-                        onEndListener.logAction, Direction.UP, index,
-                        TaskUtils.getLaunchComponentKeyForTask(task.key));
-            }
-        }
-    }
-
-    public PendingAnimation createTaskDismissAnimation(TaskView taskView, boolean animateTaskView,
-            boolean shouldRemoveTask, long duration) {
-        if (FeatureFlags.IS_DOGFOOD_BUILD && mPendingAnimation != null) {
-            throw new IllegalStateException("Another pending animation is still running");
-        }
-        AnimatorSet anim = new AnimatorSet();
-        PendingAnimation pendingAnimation = new PendingAnimation(anim);
-
-        int count = getPageCount();
-        if (count == 0) {
-            return pendingAnimation;
-        }
-
-        int[] oldScroll = new int[count];
-        getPageScrolls(oldScroll, false, SIMPLE_SCROLL_LOGIC);
-
-        int[] newScroll = new int[count];
-        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, anim, duration);
-                }
-            } 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) {
-                    addAnim(ObjectAnimator.ofFloat(child, TRANSLATION_X, scrollDiff),
-                            duration, ACCEL, anim);
-                    needsCurveUpdates = true;
-                }
-            }
-        }
-
-        if (needsCurveUpdates) {
-            ValueAnimator va = ValueAnimator.ofFloat(0, 1);
-            va.addUpdateListener((a) -> updateCurveProperties());
-            anim.play(va);
-        }
-
-        // Add a tiny bit of translation Z, so that it draws on top of other views
-        if (animateTaskView) {
-            taskView.setTranslationZ(0.1f);
-        }
-
-        mPendingAnimation = pendingAnimation;
-        mPendingAnimation.addEndListener((onEndListener) -> {
-           if (onEndListener.isSuccess) {
-               if (shouldRemoveTask) {
-                   removeTask(taskView.getTask(), draggedIndex, onEndListener, true);
-               }
-               int pageToSnapTo = mCurrentPage;
-               if (draggedIndex < pageToSnapTo || pageToSnapTo == (getTaskViewCount() - 1)) {
-                   pageToSnapTo -= 1;
-               }
-               removeView(taskView);
-
-               if (getTaskViewCount() == 0) {
-                   removeView(mClearAllButton);
-                   onAllTasksRemoved();
-               } else {
-                   snapToPageImmediately(pageToSnapTo);
-               }
-           }
-           resetTaskVisuals();
-           mPendingAnimation = null;
-        });
-        return pendingAnimation;
-    }
-
-    public PendingAnimation createAllTasksDismissAnimation(long duration) {
-        if (FeatureFlags.IS_DOGFOOD_BUILD && mPendingAnimation != null) {
-            throw new IllegalStateException("Another pending animation is still running");
-        }
-        AnimatorSet anim = new AnimatorSet();
-        PendingAnimation pendingAnimation = new PendingAnimation(anim);
-
-        int count = getTaskViewCount();
-        for (int i = 0; i < count; i++) {
-            addDismissedTaskAnimations(getChildAt(i), anim, duration);
-        }
-
-        mPendingAnimation = pendingAnimation;
-        mPendingAnimation.addEndListener((onEndListener) -> {
-            if (onEndListener.isSuccess) {
-                int taskViewCount = getTaskViewCount();
-                for (int i = 0; i < taskViewCount; i++) {
-                    removeTask(getTaskViewAt(i).getTask(), -1, onEndListener, false);
-                }
-                removeAllViews();
-                onAllTasksRemoved();
-            }
-            mPendingAnimation = null;
-        });
-        return pendingAnimation;
-    }
-
-    private static void addAnim(ObjectAnimator anim, long duration,
-            TimeInterpolator interpolator, AnimatorSet set) {
-        anim.setDuration(duration).setInterpolator(interpolator);
-        set.play(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 = AnimatorPlaybackController.wrap(
-                pendingAnim.anim, DISMISS_TASK_DURATION);
-        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));
-    }
-
-    private void dismissCurrentTask() {
-        TaskView taskView = getTaskView(getNextPage());
-        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) {
-                getChildAt(i).setAlpha(alpha);
-            }
-        }
-        mClearAllButton.setContentAlpha(mContentAlpha);
-
-        int alphaInt = Math.round(alpha * 255);
-        mEmptyMessagePaint.setAlpha(alphaInt);
-        mEmptyIcon.setAlpha(alphaInt);
-
-        setVisibility(alpha > 0 ? VISIBLE : GONE);
-    }
-
-    private float[] getAdjacentScaleAndTranslation(TaskView currTask,
-            float currTaskToScale, float currTaskToTranslationY) {
-        float displacement = currTask.getWidth() * (currTaskToScale - currTask.getCurveScale());
-        sTempFloatArray[0] = currTaskToScale;
-        sTempFloatArray[1] = mIsRtl ? -displacement : displacement;
-        sTempFloatArray[2] = currTaskToTranslationY;
-        return sTempFloatArray;
-    }
-
-    @Override
-    public void onViewAdded(View child) {
-        super.onViewAdded(child);
-        child.setAlpha(mContentAlpha);
-    }
-
-    public TaskView getTaskViewAt(int index) {
-        View child = getChildAt(index);
-        return child == mClearAllButton ? null : (TaskView) child;
-    }
-
-    public void updateEmptyMessage() {
-        boolean isEmpty = getChildCount() == 0;
-        boolean hasSizeChanged = mLastMeasureSize.x != getWidth()
-                || mLastMeasureSize.y != getHeight();
-        if (isEmpty == mShowEmptyMessage && !hasSizeChanged) {
-            return;
-        }
-        setContentDescription(isEmpty ? mEmptyMessage : "");
-        mShowEmptyMessage = isEmpty;
-        updateEmptyStateUi(hasSizeChanged);
-        invalidate();
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-        updateEmptyStateUi(changed);
-
-        // Set the pivot points to match the task preview center
-        setPivotY(((mInsets.top + getPaddingTop() + mTaskTopMargin)
-                + (getHeight() - mInsets.bottom - getPaddingBottom())) / 2);
-        setPivotX(((mInsets.left + getPaddingLeft())
-                + (getWidth() - mInsets.right - getPaddingRight())) / 2);
-    }
-
-    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, ClipAnimationHelper clipAnimationHelper) {
-        AnimatorSet anim = new AnimatorSet();
-
-        int taskIndex = indexOfChild(tv);
-        int centerTaskIndex = getCurrentPage();
-        boolean launchingCenterTask = taskIndex == centerTaskIndex;
-
-        float toScale = clipAnimationHelper.getSourceRect().width()
-                / clipAnimationHelper.getTargetRect().width();
-        float toTranslationY = clipAnimationHelper.getSourceRect().centerY()
-                - clipAnimationHelper.getTargetRect().centerY();
-        if (launchingCenterTask) {
-            TaskView centerTask = getTaskViewAt(centerTaskIndex);
-            if (taskIndex - 1 >= 0) {
-                TaskView adjacentTask = getTaskViewAt(taskIndex - 1);
-                float[] scaleAndTranslation = getAdjacentScaleAndTranslation(centerTask,
-                        toScale, toTranslationY);
-                scaleAndTranslation[1] = -scaleAndTranslation[1];
-                anim.play(createAnimForChild(adjacentTask, scaleAndTranslation));
-            }
-            if (taskIndex + 1 < getTaskViewCount()) {
-                TaskView adjacentTask = getTaskViewAt(taskIndex + 1);
-                float[] scaleAndTranslation = getAdjacentScaleAndTranslation(centerTask,
-                        toScale, toTranslationY);
-                anim.play(createAnimForChild(adjacentTask, scaleAndTranslation));
-            }
-        } 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(ObjectAnimator.ofPropertyValuesHolder(getPageAt(otherAdjacentTaskIndex),
-                        new PropertyListBuilder()
-                                .translationX(mIsRtl ? -displacementX : displacementX)
-                                .scale(1)
-                                .build()));
-            }
-        }
-        return anim;
-    }
-
-    private Animator createAnimForChild(TaskView child, float[] toScaleAndTranslation) {
-        AnimatorSet anim = new AnimatorSet();
-        anim.play(ObjectAnimator.ofFloat(child, TaskView.ZOOM_SCALE, toScaleAndTranslation[0]));
-        anim.play(ObjectAnimator.ofPropertyValuesHolder(child,
-                        new PropertyListBuilder()
-                                .translationX(toScaleAndTranslation[1])
-                                .translationY(toScaleAndTranslation[2])
-                                .build()));
-        return anim;
-    }
-
-    public PendingAnimation createTaskLauncherAnimation(TaskView tv, long duration) {
-        if (FeatureFlags.IS_DOGFOOD_BUILD && mPendingAnimation != null) {
-            throw new IllegalStateException("Another pending animation is still running");
-        }
-
-        int count = getChildCount();
-        if (count == 0) {
-            return new PendingAnimation(new AnimatorSet());
-        }
-
-        tv.setVisibility(INVISIBLE);
-        int targetSysUiFlags = tv.getThumbnail().getSysUiStatusNavFlags();
-        TaskViewDrawable drawable = new TaskViewDrawable(tv, this);
-        getOverlay().add(drawable);
-
-        final boolean[] passedOverviewThreshold = new boolean[] {false};
-        ObjectAnimator drawableAnim =
-                ObjectAnimator.ofFloat(drawable, TaskViewDrawable.PROGRESS, 1, 0);
-        drawableAnim.setInterpolator(LINEAR);
-        drawableAnim.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);
-
-            // Passing the threshold from taskview to fullscreen app will vibrate
-            final boolean passed = animator.getAnimatedFraction() >= MIN_PROGRESS_FOR_OVERVIEW;
-            if (passed != passedOverviewThreshold[0]) {
-                passedOverviewThreshold[0] = passed;
-                performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
-                        HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
-            }
-        });
-
-        AnimatorSet anim = createAdjacentPageAnimForTaskLaunch(tv,
-                drawable.getClipAnimationHelper());
-        anim.play(drawableAnim);
-        anim.setDuration(duration);
-
-        Consumer<Boolean> onTaskLaunchFinish = (result) -> {
-            onTaskLaunched(result);
-            tv.setVisibility(VISIBLE);
-            getOverlay().remove(drawable);
-        };
-
-        mPendingAnimation = new PendingAnimation(anim);
-        mPendingAnimation.addEndListener((onEndListener) -> {
-            if (onEndListener.isSuccess) {
-                Consumer<Boolean> onLaunchResult = (result) -> {
-                    onTaskLaunchFinish.accept(result);
-                    if (!result) {
-                        tv.notifyTaskLaunchFailed(TAG);
-                    }
-                };
-                tv.launchTask(false, onLaunchResult, getHandler());
-                Task task = tv.getTask();
-                if (task != null) {
-                    mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
-                            onEndListener.logAction, Direction.DOWN, indexOfChild(tv),
-                            TaskUtils.getLaunchComponentKeyForTask(task.key));
-                }
-            } else {
-                onTaskLaunchFinish.accept(false);
-            }
-            mPendingAnimation = null;
-        });
-        return mPendingAnimation;
-    }
-
-    public abstract boolean shouldUseMultiWindowTaskSizeStrategy();
-
-    protected void onTaskLaunched(boolean success) {
-        resetTaskVisuals();
-    }
-
-    @Override
-    protected void notifyPageSwitchListener(int prevPage) {
-        super.notifyPageSwitchListener(prevPage);
-        loadVisibleTaskData();
-    }
-
-    @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] - 1);
-            event.setToIndex(taskViewCount - visibleTasks[0] - 1);
-            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;
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
index 8b5e832..63c8023 100644
--- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
+++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
@@ -15,10 +15,13 @@
  */
 package com.android.quickstep.views;
 
-import static android.support.v4.graphics.ColorUtils.setAlphaComponent;
+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.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 android.content.Context;
 import android.graphics.Canvas;
@@ -27,14 +30,20 @@
 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.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.uioverrides.states.OverviewState;
 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;
 
 /**
  * Scrim used for all-apps and shelf in Overview
@@ -43,12 +52,15 @@
  *    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 {
+public class ShelfScrimView extends ScrimView 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;
 
@@ -62,6 +74,9 @@
     private int mMidAlpha;
     private float mMidProgress;
 
+    private Interpolator mBeforeMidProgressColorInterpolator = ACCEL;
+    private Interpolator mAfterMidProgressColorInterpolator = ACCEL;
+
     private float mShiftRange;
 
     private final float mShelfOffset;
@@ -76,12 +91,14 @@
     private final Path mRemainingScreenPath = new Path();
     private boolean mRemainingScreenPathValid = false;
 
+    private Mode mSysUINavigationMode;
+
     public ShelfScrimView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mMaxScrimAlpha = Math.round(OVERVIEW.getWorkspaceScrimAlpha(mLauncher) * 255);
 
         mEndAlpha = Color.alpha(mEndScrim);
-        mRadius = mLauncher.getResources().getDimension(R.dimen.shelf_surface_radius);
+        mRadius = BOTTOM_CORNER_RADIUS_RATIO * Themes.getDialogCornerRadius(context);
         mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
 
         mShelfOffset = context.getResources().getDimension(R.dimen.shelf_surface_offset);
@@ -96,6 +113,33 @@
     }
 
     @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;
+        } else {
+            mBeforeMidProgressColorInterpolator = ACCEL;
+            mAfterMidProgressColorInterpolator = Interpolators.clampToProgress(ACCEL, 0.5f, 1f);
+        }
+    }
+
+    @Override
     public void reInitUi() {
         DeviceProfile dp = mLauncher.getDeviceProfile();
         mDrawingFlatColor = dp.isVerticalBarLayout();
@@ -104,14 +148,22 @@
             mRemainingScreenPathValid = false;
             mShiftRange = mLauncher.getAllAppsController().getShiftRange();
 
-            mMidProgress = OVERVIEW.getVerticalProgress(mLauncher);
-            mMidAlpha = mMidProgress >= 1 ? 0
-                    : Themes.getAttrInteger(getContext(), R.attr.allAppsInterimScrimAlpha);
+            if ((OVERVIEW.getVisibleElements(mLauncher) & ALL_APPS_HEADER_EXTRA) == 0) {
+                mMidProgress = 1;
+                mMidAlpha = 0;
+            } else {
+                mMidAlpha = Themes.getAttrInteger(getContext(), R.attr.allAppsInterimScrimAlpha);
+                Rect hotseatPadding = dp.getHotseatLayoutPadding();
+                int hotseatSize = dp.hotseatBarSizePx + dp.getInsets().bottom
+                        - hotseatPadding.bottom - hotseatPadding.top;
+                float arrowTop = Math.min(hotseatSize, OverviewState.getDefaultSwipeHeight(dp));
+                mMidProgress =  1 - (arrowTop / mShiftRange);
 
+            }
             mTopOffset = dp.getInsets().top - mShelfOffset;
             mShelfTopAtThreshold = mShiftRange * SCRIM_CATCHUP_THRESHOLD + mTopOffset;
-            updateColors();
         }
+        updateColors();
         updateDragHandleAlpha();
         invalidate();
     }
@@ -135,25 +187,30 @@
         if (mProgress >= 1) {
             mRemainingScreenColor = 0;
             mShelfColor = 0;
+            if (mSysUINavigationMode == Mode.NO_BUTTON
+                    && mLauncher.getStateManager().getState() == BACKGROUND_APP) {
+                // 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, ACCEL));
-            mShelfColor = setAlphaComponent(mEndScrim, alpha);
+                    mProgress, mMidProgress, 1, mMidAlpha, 0, mBeforeMidProgressColorInterpolator));
+            mShelfColor = setColorAlphaBound(mEndScrim, alpha);
         } else {
             mDragHandleOffset += mShiftRange * (mMidProgress - mProgress);
 
             // 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, Interpolators.clampToProgress(ACCEL, 0.5f, 1f)));
-            mShelfColor = setAlphaComponent(mEndScrim, alpha);
+                            (float) mMidAlpha, mAfterMidProgressColorInterpolator));
+            mShelfColor = setColorAlphaBound(mEndScrim, alpha);
 
             int remainingScrimAlpha = Math.round(
                     Utilities.mapToRange(mProgress, (float) 0, mMidProgress, mMaxScrimAlpha,
                             (float) 0, LINEAR));
-            mRemainingScreenColor = setAlphaComponent(mScrimColor, remainingScrimAlpha);
+            mRemainingScreenColor = setColorAlphaBound(mScrimColor, remainingScrimAlpha);
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
deleted file mode 100644
index 6eb6854..0000000
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quickstep.views;
-
-import static com.android.quickstep.views.TaskThumbnailView.DIM_ALPHA;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.ViewGroup;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-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.AnimationSuccessListener;
-import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
-import com.android.launcher3.views.BaseDragLayer;
-import com.android.quickstep.TaskSystemShortcut;
-import com.android.quickstep.TaskUtils;
-
-/**
- * Contains options for a recent task when long-pressing its icon.
- */
-public class TaskMenuView extends AbstractFloatingView {
-
-    private static final Rect sTempRect = new Rect();
-
-    /** Note that these will be shown in order from top to bottom, if available for the task. */
-    public static final TaskSystemShortcut[] MENU_OPTIONS = new TaskSystemShortcut[] {
-            new TaskSystemShortcut.AppInfo(),
-            new TaskSystemShortcut.SplitScreen(),
-            new TaskSystemShortcut.Pin(),
-            new TaskSystemShortcut.Install(),
-    };
-
-    private static final int REVEAL_OPEN_DURATION = 150;
-    private static final int REVEAL_CLOSE_DURATION = 100;
-
-    private BaseDraggingActivity mActivity;
-    private TextView mTaskIconAndName;
-    private AnimatorSet mOpenCloseAnimator;
-    private TaskView mTaskView;
-    private LinearLayout mOptionLayout;
-
-    public TaskMenuView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public TaskMenuView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-
-        mActivity = BaseDraggingActivity.fromContext(context);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mTaskIconAndName = findViewById(R.id.task_icon_and_name);
-        mOptionLayout = findViewById(R.id.menu_option_layout);
-    }
-
-    @Override
-    public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
-        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            BaseDragLayer dl = mActivity.getDragLayer();
-            if (!dl.isEventOverView(this, ev)) {
-                // TODO: log this once we have a new container type for it?
-                close(true);
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    protected void handleClose(boolean animate) {
-        if (animate) {
-            animateClose();
-        } else {
-            closeComplete();
-        }
-    }
-
-    @Override
-    public void logActionCommand(int command) {
-        // TODO
-    }
-
-    @Override
-    protected boolean isOfType(int type) {
-        return (type & TYPE_TASK_MENU) != 0;
-    }
-
-    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);
-    }
-
-    private boolean populateAndShowForTask(TaskView taskView) {
-        if (isAttachedToWindow()) {
-            return false;
-        }
-        mActivity.getDragLayer().addView(this);
-        mTaskView = taskView;
-        addMenuOptions(mTaskView);
-        orientAroundTaskView(mTaskView);
-        post(this::animateOpen);
-        return true;
-    }
-
-    private void addMenuOptions(TaskView taskView) {
-        Drawable icon = taskView.getTask().icon.getConstantState().newDrawable();
-        int iconSize = getResources().getDimensionPixelSize(R.dimen.task_thumbnail_icon_size);
-        icon.setBounds(0, 0, iconSize, iconSize);
-        mTaskIconAndName.setCompoundDrawables(null, icon, null, null);
-        mTaskIconAndName.setText(TaskUtils.getTitle(getContext(), taskView.getTask()));
-        mTaskIconAndName.setOnClickListener(v -> close(true));
-
-        // Move the icon and text up half an icon size to lay over the TaskView
-        LinearLayout.LayoutParams params =
-                (LinearLayout.LayoutParams) mTaskIconAndName.getLayoutParams();
-        params.topMargin = (int) -getResources().getDimension(R.dimen.task_thumbnail_top_margin);
-        mTaskIconAndName.setLayoutParams(params);
-
-        for (TaskSystemShortcut menuOption : MENU_OPTIONS) {
-            OnClickListener onClickListener = menuOption.getOnClickListener(mActivity, taskView);
-            if (onClickListener != null) {
-                addMenuOption(menuOption, onClickListener);
-            }
-        }
-    }
-
-    private void addMenuOption(TaskSystemShortcut menuOption, OnClickListener onClickListener) {
-        ViewGroup menuOptionView = (ViewGroup) mActivity.getLayoutInflater().inflate(
-                R.layout.task_view_menu_option, this, false);
-        menuOptionView.findViewById(R.id.icon).setBackgroundResource(menuOption.iconResId);
-        ((TextView) menuOptionView.findViewById(R.id.text)).setText(menuOption.labelResId);
-        menuOptionView.setOnClickListener(onClickListener);
-        mOptionLayout.addView(menuOptionView);
-    }
-
-    private void orientAroundTaskView(TaskView taskView) {
-        measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
-        mActivity.getDragLayer().getDescendantRectRelativeToSelf(taskView, sTempRect);
-        Rect insets = mActivity.getDragLayer().getInsets();
-        BaseDragLayer.LayoutParams params = (BaseDragLayer.LayoutParams) getLayoutParams();
-        params.width = sTempRect.width();
-        params.gravity = Gravity.LEFT;
-        setLayoutParams(params);
-        setX(sTempRect.left - insets.left);
-        setY(sTempRect.top + getResources().getDimension(R.dimen.task_thumbnail_top_margin)
-                - insets.top);
-    }
-
-    private void animateOpen() {
-        animateOpenOrClosed(false);
-        mIsOpen = true;
-    }
-
-    private void animateClose() {
-        animateOpenOrClosed(true);
-    }
-
-    private void animateOpenOrClosed(boolean closing) {
-        if (mOpenCloseAnimator != null && mOpenCloseAnimator.isRunning()) {
-            return;
-        }
-        mOpenCloseAnimator = LauncherAnimUtils.createAnimatorSet();
-
-        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.addListener(new AnimationSuccessListener() {
-            @Override
-            public void onAnimationStart(Animator animation) {
-                setVisibility(VISIBLE);
-            }
-
-            @Override
-            public void onAnimationSuccess(Animator animator) {
-                if (closing) {
-                    closeComplete();
-                }
-            }
-        });
-        mOpenCloseAnimator.play(ObjectAnimator.ofFloat(this, ALPHA, closing ? 0 : 1));
-        mOpenCloseAnimator.setDuration(closing ? REVEAL_CLOSE_DURATION: REVEAL_OPEN_DURATION);
-        mOpenCloseAnimator.start();
-    }
-
-    private void closeComplete() {
-        mIsOpen = false;
-        mActivity.getDragLayer().removeView(this);
-    }
-
-    private RoundedRectRevealOutlineProvider createOpenCloseOutlineProvider() {
-        float radius = getResources().getDimension(R.dimen.task_corner_radius);
-        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/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
deleted file mode 100644
index fb653cf..0000000
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ /dev/null
@@ -1,333 +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 com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_FULLSCREEN;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.LightingColorFilter;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.Shader;
-import android.util.AttributeSet;
-import android.util.FloatProperty;
-import android.util.Property;
-import android.view.View;
-
-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.util.SystemUiController;
-import com.android.launcher3.util.Themes;
-import com.android.quickstep.TaskOverlayFactory;
-import com.android.quickstep.TaskOverlayFactory.TaskOverlay;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-
-/**
- * A task in the Recents view.
- */
-public class TaskThumbnailView extends View {
-
-    private static final LightingColorFilter[] sDimFilterCache = new LightingColorFilter[256];
-    private static final LightingColorFilter[] sHighlightFilterCache = new LightingColorFilter[256];
-
-    public static final Property<TaskThumbnailView, Float> DIM_ALPHA_MULTIPLIER =
-            new FloatProperty<TaskThumbnailView>("dimAlphaMultiplier") {
-                @Override
-                public void setValue(TaskThumbnailView thumbnail, float dimAlphaMultiplier) {
-                    thumbnail.setDimAlphaMultipler(dimAlphaMultiplier);
-                }
-
-                @Override
-                public Float get(TaskThumbnailView thumbnailView) {
-                    return thumbnailView.mDimAlphaMultiplier;
-                }
-            };
-
-    public static final Property<TaskThumbnailView, Float> DIM_ALPHA =
-            new FloatProperty<TaskThumbnailView>("dimAlpha") {
-                @Override
-                public void setValue(TaskThumbnailView thumbnail, float dimAlpha) {
-                    thumbnail.setDimAlpha(dimAlpha);
-                }
-
-                @Override
-                public Float get(TaskThumbnailView thumbnailView) {
-                    return thumbnailView.mDimAlpha;
-                }
-            };
-
-    private final float mCornerRadius;
-
-    private final BaseActivity mActivity;
-    private final TaskOverlay mOverlay;
-    private final boolean mIsDarkTextTheme;
-    private final Paint mPaint = new Paint();
-    private final Paint mBackgroundPaint = new Paint();
-
-    private final Matrix mMatrix = new Matrix();
-
-    private float mClipBottom = -1;
-
-    private Task mTask;
-    private ThumbnailData mThumbnailData;
-    protected BitmapShader mBitmapShader;
-
-    private float mDimAlpha = 1f;
-    private float mDimAlphaMultiplier = 1f;
-
-    public TaskThumbnailView(Context context) {
-        this(context, null);
-    }
-
-    public TaskThumbnailView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public TaskThumbnailView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        mCornerRadius = getResources().getDimension(R.dimen.task_corner_radius);
-        mOverlay = TaskOverlayFactory.get(context).createOverlay(this);
-        mPaint.setFilterBitmap(true);
-        mBackgroundPaint.setColor(Color.WHITE);
-        mActivity = BaseActivity.fromContext(context);
-        mIsDarkTextTheme = Themes.getAttrBoolean(mActivity, R.attr.isWorkspaceDarkText);
-    }
-
-    public void bind() {
-        mOverlay.reset();
-    }
-
-    /**
-     * Updates this thumbnail.
-     */
-    public void setThumbnail(Task task, ThumbnailData thumbnailData) {
-        mTask = task;
-        int color = task == null ? Color.BLACK : task.colorBackground | 0xFF000000;
-        mPaint.setColor(color);
-        mBackgroundPaint.setColor(color);
-
-        if (thumbnailData != null && thumbnailData.thumbnail != null) {
-            Bitmap bm = thumbnailData.thumbnail;
-            bm.prepareToDraw();
-            mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
-            mPaint.setShader(mBitmapShader);
-            mThumbnailData = thumbnailData;
-            updateThumbnailMatrix();
-        } else {
-            mBitmapShader = null;
-            mThumbnailData = null;
-            mPaint.setShader(null);
-            mOverlay.reset();
-        }
-        updateThumbnailPaintFilter();
-    }
-
-    public void setDimAlphaMultipler(float dimAlphaMultipler) {
-        mDimAlphaMultiplier = dimAlphaMultipler;
-        setDimAlpha(mDimAlpha);
-    }
-
-    /**
-     * Sets the alpha of the dim layer on top of this view.
-     *
-     * If dimAlpha is 0, no dimming is applied; if dimAlpha is 1, the thumbnail will be black.
-     */
-    public void setDimAlpha(float dimAlpha) {
-        mDimAlpha = dimAlpha;
-        updateThumbnailPaintFilter();
-    }
-
-    public float getDimAlpha() {
-        return mDimAlpha;
-    }
-
-    public Rect getInsets() {
-        if (mThumbnailData != null) {
-            return mThumbnailData.insets;
-        }
-        return new Rect();
-    }
-
-    public int getSysUiStatusNavFlags() {
-        if (mThumbnailData != null) {
-            int flags = 0;
-            flags |= (mThumbnailData.systemUiVisibility & SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0
-                    ? SystemUiController.FLAG_LIGHT_STATUS
-                    : SystemUiController.FLAG_DARK_STATUS;
-            flags |= (mThumbnailData.systemUiVisibility & SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR) != 0
-                    ? SystemUiController.FLAG_LIGHT_NAV
-                    : SystemUiController.FLAG_DARK_NAV;
-            return flags;
-        }
-        return 0;
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        drawOnCanvas(canvas, 0, 0, getMeasuredWidth(), getMeasuredHeight(), mCornerRadius);
-    }
-
-    public float getCornerRadius() {
-        return mCornerRadius;
-    }
-
-    public void drawOnCanvas(Canvas canvas, float x, float y, float width, float height,
-            float cornerRadius) {
-        // 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 || mClipBottom > 0 || mThumbnailData.isTranslucent) {
-            canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mBackgroundPaint);
-            if (drawBackgroundOnly) {
-                return;
-            }
-        }
-
-        if (mClipBottom > 0) {
-            canvas.save();
-            canvas.clipRect(x, y, width, mClipBottom);
-            canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mPaint);
-            canvas.restore();
-        } else {
-            canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mPaint);
-        }
-    }
-
-    private void updateThumbnailPaintFilter() {
-        int mul = (int) ((1 - mDimAlpha * mDimAlphaMultiplier) * 255);
-        if (mBitmapShader != null) {
-            LightingColorFilter filter = getDimmingColorFilter(mul, mIsDarkTextTheme);
-            mPaint.setColorFilter(filter);
-            mBackgroundPaint.setColorFilter(filter);
-        } else {
-            mPaint.setColorFilter(null);
-            mPaint.setColor(Color.argb(255, mul, mul, mul));
-        }
-        invalidate();
-    }
-
-    private void updateThumbnailMatrix() {
-        boolean rotate = false;
-        mClipBottom = -1;
-        if (mBitmapShader != null && mThumbnailData != null) {
-            float scale = mThumbnailData.scale;
-            Rect thumbnailInsets  = mThumbnailData.insets;
-            final float thumbnailWidth = mThumbnailData.thumbnail.getWidth() -
-                    (thumbnailInsets.left + thumbnailInsets.right) * scale;
-            final float thumbnailHeight = mThumbnailData.thumbnail.getHeight() -
-                    (thumbnailInsets.top + thumbnailInsets.bottom) * scale;
-
-            final float thumbnailScale;
-            final DeviceProfile profile = mActivity.getDeviceProfile();
-
-            if (getMeasuredWidth() == 0) {
-                // If we haven't measured , skip the thumbnail drawing and only draw the background
-                // color
-                thumbnailScale = 0f;
-            } else {
-                final Configuration configuration =
-                        getContext().getResources().getConfiguration();
-                // Rotate the screenshot if not in multi-window mode
-                rotate = FeatureFlags.OVERVIEW_USE_SCREENSHOT_ORIENTATION &&
-                        configuration.orientation != mThumbnailData.orientation &&
-                        !mActivity.isInMultiWindowModeCompat() &&
-                        mThumbnailData.windowingMode == WINDOWING_MODE_FULLSCREEN;
-                // Scale the screenshot to always fit the width of the card.
-                thumbnailScale = rotate
-                        ? getMeasuredWidth() / thumbnailHeight
-                        : getMeasuredWidth() / thumbnailWidth;
-            }
-
-            if (rotate) {
-                int rotationDir = profile.isVerticalBarLayout() && !profile.isSeascape() ? -1 : 1;
-                mMatrix.setRotate(90 * rotationDir);
-                int newLeftInset = rotationDir == 1 ? thumbnailInsets.bottom : thumbnailInsets.top;
-                int newTopInset = rotationDir == 1 ? thumbnailInsets.left : thumbnailInsets.right;
-                mMatrix.postTranslate(-newLeftInset * scale, -newTopInset * scale);
-                if (rotationDir == -1) {
-                    // Crop the right/bottom side of the screenshot rather than left/top
-                    float excessHeight = thumbnailWidth * thumbnailScale - getMeasuredHeight();
-                    mMatrix.postTranslate(0, -excessHeight);
-                }
-                // Move the screenshot to the thumbnail window (rotation moved it out).
-                if (rotationDir == 1) {
-                    mMatrix.postTranslate(mThumbnailData.thumbnail.getHeight(), 0);
-                } else {
-                    mMatrix.postTranslate(0, mThumbnailData.thumbnail.getWidth());
-                }
-            } else {
-                mMatrix.setTranslate(-mThumbnailData.insets.left * scale,
-                        -mThumbnailData.insets.top * scale);
-            }
-            mMatrix.postScale(thumbnailScale, thumbnailScale);
-            mBitmapShader.setLocalMatrix(mMatrix);
-
-            float bitmapHeight = Math.max((rotate ? thumbnailWidth : thumbnailHeight)
-                    * thumbnailScale, 0);
-            if (Math.round(bitmapHeight) < getMeasuredHeight()) {
-                mClipBottom = bitmapHeight;
-            }
-            mPaint.setShader(mBitmapShader);
-        }
-
-        if (rotate) {
-            // The overlay doesn't really work when the screenshot is rotated, so don't add it.
-            mOverlay.reset();
-        } else {
-            mOverlay.setTaskInfo(mTask, mThumbnailData, mMatrix);
-        }
-        invalidate();
-    }
-
-    @Override
-    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-        super.onSizeChanged(w, h, oldw, oldh);
-        updateThumbnailMatrix();
-    }
-
-    private static LightingColorFilter getDimmingColorFilter(int intensity, boolean shouldLighten) {
-        intensity = Utilities.boundToRange(intensity, 0, 255);
-        if (intensity == 255) {
-            return null;
-        }
-        if (shouldLighten) {
-            if (sHighlightFilterCache[intensity] == null) {
-                int colorAdd = 255 - intensity;
-                sHighlightFilterCache[intensity] = new LightingColorFilter(
-                        Color.argb(255, intensity, intensity, intensity),
-                        Color.argb(255, colorAdd, colorAdd, colorAdd));
-            }
-            return sHighlightFilterCache[intensity];
-        } else {
-            if (sDimFilterCache[intensity] == null) {
-                sDimFilterCache[intensity] = new LightingColorFilter(
-                        Color.argb(255, intensity, intensity, intensity), 0);
-            }
-            return sDimFilterCache[intensity];
-        }
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
deleted file mode 100644
index 508e5bb..0000000
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ /dev/null
@@ -1,365 +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.widget.Toast.LENGTH_SHORT;
-
-import static com.android.quickstep.views.TaskThumbnailView.DIM_ALPHA_MULTIPLIER;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
-import android.app.ActivityOptions;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Outline;
-import android.os.Bundle;
-import android.os.Handler;
-import android.util.AttributeSet;
-import android.util.FloatProperty;
-import android.util.Log;
-import android.util.Property;
-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.BaseActivity;
-import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.R;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.quickstep.TaskSystemShortcut;
-import com.android.quickstep.TaskUtils;
-import com.android.quickstep.views.RecentsView.PageCallbacks;
-import com.android.quickstep.views.RecentsView.ScrollState;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.Task.TaskCallbacks;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-
-import java.util.function.Consumer;
-
-/**
- * A task in the Recents view.
- */
-public class TaskView extends FrameLayout implements TaskCallbacks, PageCallbacks {
-
-    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.
-     */
-    private 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;
-
-    public static final Property<TaskView, Float> ZOOM_SCALE =
-            new FloatProperty<TaskView>("zoomScale") {
-                @Override
-                public void setValue(TaskView taskView, float v) {
-                    taskView.setZoomScale(v);
-                }
-
-                @Override
-                public Float get(TaskView taskView) {
-                    return taskView.mZoomScale;
-                }
-            };
-
-    private Task mTask;
-    private TaskThumbnailView mSnapshotView;
-    private IconView mIconView;
-    private float mCurveScale;
-    private float mZoomScale;
-    private Animator mDimAlphaAnim;
-
-    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);
-        setOnClickListener((view) -> {
-            if (getTask() == null) {
-                return;
-            }
-            launchTask(true /* animate */);
-            BaseActivity.fromContext(context).getUserEventDispatcher().logTaskLaunchOrDismiss(
-                    Touch.TAP, Direction.NONE, getRecentsView().indexOfChild(this),
-                    TaskUtils.getLaunchComponentKeyForTask(getTask().key));
-        });
-        setOutlineProvider(new TaskOutlineProvider(getResources()));
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mSnapshotView = findViewById(R.id.snapshot);
-        mIconView = findViewById(R.id.icon);
-    }
-
-    /**
-     * Updates this task view to the given {@param task}.
-     */
-    public void bind(Task task) {
-        if (mTask != null) {
-            mTask.removeCallback(this);
-        }
-        mTask = task;
-        mSnapshotView.bind();
-        task.addCallback(this);
-        setContentDescription(task.titleDescription);
-    }
-
-    public Task getTask() {
-        return mTask;
-    }
-
-    public TaskThumbnailView getThumbnail() {
-        return mSnapshotView;
-    }
-
-    public IconView getIconView() {
-        return mIconView;
-    }
-
-    public void launchTask(boolean animate) {
-        launchTask(animate, (result) -> {
-            if (!result) {
-                notifyTaskLaunchFailed(TAG);
-            }
-        }, getHandler());
-    }
-
-    public void launchTask(boolean animate, Consumer<Boolean> resultCallback,
-            Handler resultCallbackHandler) {
-        if (mTask != null) {
-            final ActivityOptions opts;
-            if (animate) {
-                opts = BaseDraggingActivity.fromContext(getContext())
-                        .getActivityLaunchOptions(this);
-            } else {
-                opts = ActivityOptions.makeCustomAnimation(getContext(), 0, 0);
-            }
-            ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(mTask.key,
-                    opts, resultCallback, resultCallbackHandler);
-        }
-    }
-
-    @Override
-    public void onTaskDataLoaded(Task task, ThumbnailData thumbnailData) {
-        mSnapshotView.setThumbnail(task, thumbnailData);
-        mIconView.setDrawable(task.icon);
-        mIconView.setOnClickListener(icon -> TaskMenuView.showForTask(this));
-        mIconView.setOnLongClickListener(icon -> {
-            requestDisallowInterceptTouchEvent(true);
-            return TaskMenuView.showForTask(this);
-        });
-    }
-
-    @Override
-    public void onTaskDataUnloaded() {
-        mSnapshotView.setThumbnail(null, null);
-        mIconView.setDrawable(null);
-        mIconView.setOnLongClickListener(null);
-    }
-
-    @Override
-    public void onTaskWindowingModeChanged() {
-        // Do nothing
-    }
-
-    public void animateIconToScaleAndDim(float scale) {
-        mIconView.animate().scaleX(scale).scaleY(scale).setDuration(SCALE_ICON_DURATION).start();
-        mDimAlphaAnim = ObjectAnimator.ofFloat(mSnapshotView, DIM_ALPHA_MULTIPLIER, 1 - scale,
-                scale);
-        mDimAlphaAnim.setDuration(DIM_ANIM_DURATION);
-        mDimAlphaAnim.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mDimAlphaAnim = null;
-            }
-        });
-        mDimAlphaAnim.start();
-    }
-
-    protected void setIconScaleAndDim(float iconScale) {
-        mIconView.animate().cancel();
-        mIconView.setScaleX(iconScale);
-        mIconView.setScaleY(iconScale);
-        if (mDimAlphaAnim != null) {
-            mDimAlphaAnim.cancel();
-        }
-        mSnapshotView.setDimAlphaMultipler(iconScale);
-    }
-
-    public void resetVisualProperties() {
-        setZoomScale(1);
-        setTranslationX(0f);
-        setTranslationY(0f);
-        setTranslationZ(0);
-        setAlpha(1f);
-        setIconScaleAndDim(1);
-    }
-
-    @Override
-    public void onPageScroll(ScrollState scrollState) {
-        float curveInterpolation =
-                CURVE_INTERPOLATOR.getInterpolation(scrollState.linearInterpolation);
-
-        mSnapshotView.setDimAlpha(curveInterpolation * MAX_PAGE_SCRIM_ALPHA);
-        setCurveScale(getCurveScaleForCurveInterpolation(curveInterpolation));
-    }
-
-    @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);
-    }
-
-    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;
-        onScaleChanged();
-    }
-
-    public float getCurveScale() {
-        return mCurveScale;
-    }
-
-    public void setZoomScale(float adjacentScale) {
-        mZoomScale = adjacentScale;
-        onScaleChanged();
-    }
-
-    private void onScaleChanged() {
-        float scale = mCurveScale * mZoomScale;
-        setScaleX(scale);
-        setScaleY(scale);
-    }
-
-    @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 final float mRadius;
-
-        TaskOutlineProvider(Resources res) {
-            mMarginTop = res.getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
-            mRadius = res.getDimension(R.dimen.task_corner_radius);
-        }
-
-        @Override
-        public void getOutline(View view, Outline outline) {
-            outline.setRoundRect(0, mMarginTop, view.getWidth(),
-                    view.getHeight(), mRadius);
-        }
-    }
-
-    @Override
-    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfo(info);
-
-        info.addAction(
-                new AccessibilityNodeInfo.AccessibilityAction(R.string.accessibility_close_task,
-                        getContext().getText(R.string.accessibility_close_task)));
-
-        final Context context = getContext();
-        final BaseDraggingActivity activity = BaseDraggingActivity.fromContext(context);
-        for (TaskSystemShortcut menuOption : TaskMenuView.MENU_OPTIONS) {
-            OnClickListener onClickListener = menuOption.getOnClickListener(activity, this);
-            if (onClickListener != null) {
-                info.addAction(new AccessibilityNodeInfo.AccessibilityAction(menuOption.labelResId,
-                        context.getText(menuOption.labelResId)));
-            }
-        }
-
-        final RecentsView recentsView = getRecentsView();
-        final AccessibilityNodeInfo.CollectionItemInfo itemInfo =
-                AccessibilityNodeInfo.CollectionItemInfo.obtain(
-                        0, 1, recentsView.getChildCount() - recentsView.indexOfChild(this) - 1, 1,
-                        false);
-        info.setCollectionItemInfo(itemInfo);
-    }
-
-    @Override
-    public boolean performAccessibilityAction(int action, Bundle arguments) {
-        if (action == R.string.accessibility_close_task) {
-            getRecentsView().dismissTask(this, true /*animateTaskView*/,
-                    true /*removeTask*/);
-            return true;
-        }
-
-        for (TaskSystemShortcut menuOption : TaskMenuView.MENU_OPTIONS) {
-            if (action == menuOption.labelResId) {
-                OnClickListener onClickListener = menuOption.getOnClickListener(
-                        BaseDraggingActivity.fromContext(getContext()), this);
-                if (onClickListener != null) {
-                    onClickListener.onClick(this);
-                }
-                return true;
-            }
-        }
-
-        return super.performAccessibilityAction(action, arguments);
-    }
-
-    private RecentsView getRecentsView() {
-        return (RecentsView) getParent();
-    }
-
-    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();
-    }
-}
diff --git a/quickstep/tests/OWNERS b/quickstep/tests/OWNERS
new file mode 100644
index 0000000..046d871
--- /dev/null
+++ b/quickstep/tests/OWNERS
@@ -0,0 +1 @@
+vadimt@google.com
diff --git a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
new file mode 100644
index 0000000..a412b39
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
@@ -0,0 +1,34 @@
+/*
+ * 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 com.android.launcher3.ui.AbstractLauncherUiTest;
+
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
+
+/**
+ * Base class for all instrumentation tests that deal with Quickstep.
+ */
+public abstract class AbstractQuickStepTest extends AbstractLauncherUiTest {
+    @Override
+    protected TestRule getRulesInsideActivityMonitor() {
+        return RuleChain.
+                outerRule(new NavigationModeSwitchRule(mLauncher)).
+                around(super.getRulesInsideActivityMonitor());
+    }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java
new file mode 100644
index 0000000..d9fcf4d
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java
@@ -0,0 +1,163 @@
+/**
+ * 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.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.compat.LauncherAppsCompat;
+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 = LauncherAppsCompat.getInstance(mTargetContext)
+                .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() {
+        mActivityMonitor.startLauncher();
+        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() {
+        mActivityMonitor.startLauncher();
+        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() {
+        mActivityMonitor.startLauncher();
+        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
new file mode 100644
index 0000000..0c5a6f5
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
@@ -0,0 +1,95 @@
+package com.android.quickstep;
+
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.launcher3.LauncherState.OVERVIEW;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.app.PendingIntent;
+import android.app.usage.UsageStatsManager;
+import android.content.Intent;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.launcher3.Launcher;
+import com.android.quickstep.views.DigitalWellBeingToast;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskView;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.Duration;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class DigitalWellBeingToastTest extends AbstractQuickStepTest {
+    private static final String CALCULATOR_PACKAGE =
+            resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR);
+
+    @Test
+    public void testToast() throws Exception {
+        startAppFast(CALCULATOR_PACKAGE);
+
+        final UsageStatsManager usageStatsManager =
+                mTargetContext.getSystemService(UsageStatsManager.class);
+        final int observerId = 0;
+
+        try {
+            final String[] packages = new String[]{CALCULATOR_PACKAGE};
+
+            // Set time limit for app.
+            runWithShellPermission(() ->
+                    usageStatsManager.registerAppUsageLimitObserver(observerId, packages,
+                            Duration.ofSeconds(600), Duration.ofSeconds(300),
+                            PendingIntent.getActivity(mTargetContext, -1, new Intent(), 0)));
+
+            mLauncher.pressHome();
+            final DigitalWellBeingToast toast = getToast();
+
+            assertTrue("Toast is not visible", toast.hasLimit());
+            assertEquals("Toast text: ", "5 minutes left today", toast.getText());
+
+            // Unset time limit for app.
+            runWithShellPermission(
+                    () -> usageStatsManager.unregisterAppUsageLimitObserver(observerId));
+
+            mLauncher.pressHome();
+            assertFalse("Toast is visible", getToast().hasLimit());
+        } finally {
+            runWithShellPermission(
+                    () -> usageStatsManager.unregisterAppUsageLimitObserver(observerId));
+        }
+    }
+
+    private DigitalWellBeingToast getToast() {
+        executeOnLauncher(launcher -> launcher.getStateManager().goToState(OVERVIEW));
+        waitForState("Launcher internal state didn't switch to Overview", OVERVIEW);
+        waitForLauncherCondition("No latest task", launcher -> getLatestTask(launcher) != null);
+
+        return getFromLauncher(launcher -> {
+            final TaskView task = getLatestTask(launcher);
+            assertTrue("Latest task is not Calculator",
+                    CALCULATOR_PACKAGE.equals(task.getTask().getTopComponent().getPackageName()));
+            return task.getDigitalWellBeingToast();
+        });
+    }
+
+    private TaskView getLatestTask(Launcher launcher) {
+        return launcher.<RecentsView>getOverviewPanel().getTaskViewAt(0);
+    }
+
+    private void runWithShellPermission(Runnable action) {
+        getInstrumentation().getUiAutomation().adoptShellPermissionIdentity();
+        try {
+            action.run();
+        } finally {
+            getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
+        }
+
+    }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
new file mode 100644
index 0000000..e5f949b
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -0,0 +1,137 @@
+/*
+ * 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.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
+
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.launcher3.tapl.LauncherInstrumentation.WAIT_TIME_MS;
+import static com.android.launcher3.tapl.TestHelpers.getHomeIntentInPackage;
+import static com.android.launcher3.tapl.TestHelpers.getLauncherInMyProcess;
+import static com.android.launcher3.ui.AbstractLauncherUiTest.resolveSystemApp;
+import static com.android.launcher3.util.rule.ShellCommandRule.disableHeadsUpNotification;
+import static com.android.launcher3.util.rule.ShellCommandRule.getLauncherCommand;
+import static com.android.quickstep.NavigationModeSwitchRule.Mode.THREE_BUTTON;
+
+import static org.junit.Assert.assertTrue;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.os.RemoteException;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.Until;
+
+import com.android.launcher3.tapl.LauncherInstrumentation;
+import com.android.launcher3.testcomponent.TestCommandReceiver;
+import com.android.launcher3.util.rule.FailureWatcher;
+import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+import org.junit.runners.model.Statement;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+/**
+ * TODO: Fix fallback when quickstep is enabled
+ */
+public class FallbackRecentsTest {
+
+    private final UiDevice mDevice;
+    private final LauncherInstrumentation mLauncher;
+    private final ActivityInfo mOtherLauncherActivity;
+
+    @Rule
+    public final TestRule mDisableHeadsUpNotification = disableHeadsUpNotification();
+
+    @Rule
+    public final TestRule mSetLauncherCommand;
+
+    @Rule
+    public final TestRule mOrderSensitiveRules;
+
+    public FallbackRecentsTest() throws RemoteException {
+        Instrumentation instrumentation = getInstrumentation();
+        Context context = instrumentation.getContext();
+        mDevice = UiDevice.getInstance(instrumentation);
+        mDevice.setOrientationNatural();
+        mLauncher = new LauncherInstrumentation(instrumentation);
+
+        mOrderSensitiveRules = RuleChain.
+                outerRule(new NavigationModeSwitchRule(mLauncher)).
+                around(new FailureWatcher(mDevice));
+
+        mOtherLauncherActivity = context.getPackageManager().queryIntentActivities(
+                getHomeIntentInPackage(context),
+                MATCH_DISABLED_COMPONENTS).get(0).activityInfo;
+
+        mSetLauncherCommand = (base, desc) -> new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                TestCommandReceiver.callCommand(TestCommandReceiver.ENABLE_TEST_LAUNCHER);
+                UiDevice.getInstance(getInstrumentation()).executeShellCommand(
+                        getLauncherCommand(mOtherLauncherActivity));
+                try {
+                    base.evaluate();
+                } finally {
+                    TestCommandReceiver.callCommand(TestCommandReceiver.DISABLE_TEST_LAUNCHER);
+                    UiDevice.getInstance(getInstrumentation()).executeShellCommand(
+                            getLauncherCommand(getLauncherInMyProcess()));
+                }
+            }
+        };
+    }
+
+    @NavigationModeSwitch(mode = THREE_BUTTON)
+    @Test
+    public void goToOverviewFromHome() {
+        mDevice.pressHome();
+        assertTrue("Fallback Launcher not visible", mDevice.wait(Until.hasObject(By.pkg(
+                mOtherLauncherActivity.packageName)), WAIT_TIME_MS));
+
+        mLauncher.getBackground().switchToOverview();
+    }
+
+    @NavigationModeSwitch(mode = THREE_BUTTON)
+    @Test
+    public void goToOverviewFromApp() {
+        startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
+
+        mLauncher.getBackground().switchToOverview();
+    }
+
+    private void startAppFast(String packageName) {
+        final Instrumentation instrumentation = getInstrumentation();
+        final Intent intent = instrumentation.getContext().getPackageManager().
+                getLaunchIntentForPackage(packageName);
+        intent.addCategory(Intent.CATEGORY_LAUNCHER);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        instrumentation.getTargetContext().startActivity(intent);
+        assertTrue(packageName + " didn't start",
+                mDevice.wait(Until.hasObject(By.pkg(packageName).depth(0)), WAIT_TIME_MS));
+    }
+
+}
diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
new file mode 100644
index 0000000..3b35c86
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
@@ -0,0 +1,198 @@
+/*
+ * 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 androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.quickstep.NavigationModeSwitchRule.Mode.ALL;
+import static com.android.quickstep.NavigationModeSwitchRule.Mode.THREE_BUTTON;
+import static com.android.quickstep.NavigationModeSwitchRule.Mode.TWO_BUTTON;
+import static com.android.quickstep.NavigationModeSwitchRule.Mode.ZERO_BUTTON;
+import static com.android.systemui.shared.system.QuickStepContract.NAV_BAR_MODE_2BUTTON_OVERLAY;
+import static com.android.systemui.shared.system.QuickStepContract.NAV_BAR_MODE_3BUTTON_OVERLAY;
+import static com.android.systemui.shared.system.QuickStepContract.NAV_BAR_MODE_GESTURAL_OVERLAY;
+
+import android.content.Context;
+import android.util.Log;
+
+import androidx.test.uiautomator.UiDevice;
+
+import com.android.launcher3.tapl.LauncherInstrumentation;
+import com.android.launcher3.tapl.TestHelpers;
+import com.android.systemui.shared.system.QuickStepContract;
+
+import org.junit.Assert;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test rule that allows executing a test with Quickstep on and then Quickstep off.
+ * The test should be annotated with @QuickstepOnOff.
+ */
+public class NavigationModeSwitchRule implements TestRule {
+
+    static final String TAG = "QuickStepOnOffRule";
+
+    public enum Mode {
+        THREE_BUTTON, TWO_BUTTON, ZERO_BUTTON, ALL
+    }
+
+    // Annotation for tests that need to be run with quickstep enabled and disabled.
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target(ElementType.METHOD)
+    public @interface NavigationModeSwitch {
+        Mode mode() default ALL;
+    }
+
+    private final LauncherInstrumentation mLauncher;
+
+    public NavigationModeSwitchRule(LauncherInstrumentation launcher) {
+        mLauncher = launcher;
+    }
+
+    @Override
+    public Statement apply(Statement base, Description description) {
+        if (TestHelpers.isInLauncherProcess() &&
+                description.getAnnotation(NavigationModeSwitch.class) != null) {
+            Mode mode = description.getAnnotation(NavigationModeSwitch.class).mode();
+            return new Statement() {
+                @Override
+                public void evaluate() throws Throwable {
+                    final Context context = getInstrumentation().getContext();
+                    final int currentInteractionMode =
+                            LauncherInstrumentation.getCurrentInteractionMode(context);
+                    final String prevOverlayPkg =
+                            QuickStepContract.isGesturalMode(currentInteractionMode)
+                                    ? NAV_BAR_MODE_GESTURAL_OVERLAY
+                                    : QuickStepContract.isSwipeUpMode(currentInteractionMode)
+                                            ? NAV_BAR_MODE_2BUTTON_OVERLAY
+                                            : NAV_BAR_MODE_3BUTTON_OVERLAY;
+                    final LauncherInstrumentation.NavigationModel originalMode =
+                            mLauncher.getNavigationModel();
+                    try {
+                        if (mode == ZERO_BUTTON || mode == ALL) {
+                            evaluateWithZeroButtons();
+                        }
+                        if (mode == TWO_BUTTON || mode == ALL) {
+                            evaluateWithTwoButtons();
+                        }
+                        if (mode == THREE_BUTTON || mode == ALL) {
+                            evaluateWithThreeButtons();
+                        }
+                    } finally {
+                        setActiveOverlay(prevOverlayPkg, originalMode);
+                    }
+                }
+
+                public void evaluateWithoutChangingSetting(Statement base) throws Throwable {
+                    base.evaluate();
+                }
+
+                private void evaluateWithThreeButtons() throws Throwable {
+                    setActiveOverlay(NAV_BAR_MODE_3BUTTON_OVERLAY,
+                            LauncherInstrumentation.NavigationModel.THREE_BUTTON);
+                    evaluateWithoutChangingSetting(base);
+                }
+
+                private void evaluateWithTwoButtons() throws Throwable {
+                    setActiveOverlay(NAV_BAR_MODE_2BUTTON_OVERLAY,
+                            LauncherInstrumentation.NavigationModel.TWO_BUTTON);
+                    base.evaluate();
+                }
+
+                private void evaluateWithZeroButtons() throws Throwable {
+                    setActiveOverlay(NAV_BAR_MODE_GESTURAL_OVERLAY,
+                            LauncherInstrumentation.NavigationModel.ZERO_BUTTON);
+                    base.evaluate();
+                }
+
+                private void setActiveOverlay(String overlayPackage,
+                        LauncherInstrumentation.NavigationModel expectedMode) throws Exception {
+                    setOverlayPackageEnabled(NAV_BAR_MODE_3BUTTON_OVERLAY,
+                            overlayPackage == NAV_BAR_MODE_3BUTTON_OVERLAY);
+                    setOverlayPackageEnabled(NAV_BAR_MODE_2BUTTON_OVERLAY,
+                            overlayPackage == NAV_BAR_MODE_2BUTTON_OVERLAY);
+                    setOverlayPackageEnabled(NAV_BAR_MODE_GESTURAL_OVERLAY,
+                            overlayPackage == NAV_BAR_MODE_GESTURAL_OVERLAY);
+
+                    if (currentSysUiNavigationMode() != expectedMode) {
+                        final CountDownLatch latch = new CountDownLatch(1);
+                        final Context targetContext = getInstrumentation().getTargetContext();
+                        final SysUINavigationMode.NavigationModeChangeListener listener =
+                                newMode -> {
+                                    if (LauncherInstrumentation.getNavigationModel(newMode.resValue)
+                                            == expectedMode) {
+                                        latch.countDown();
+                                    }
+                                };
+                        final SysUINavigationMode sysUINavigationMode =
+                                SysUINavigationMode.INSTANCE.get(targetContext);
+                        targetContext.getMainExecutor().execute(() ->
+                                sysUINavigationMode.addModeChangeListener(listener));
+                        latch.await(10, TimeUnit.SECONDS);
+                        targetContext.getMainExecutor().execute(() ->
+                                sysUINavigationMode.removeModeChangeListener(listener));
+                        Assert.assertTrue("Navigation mode didn't change to " + expectedMode,
+                                currentSysUiNavigationMode() == expectedMode);
+                    }
+
+                    for (int i = 0; i != 100; ++i) {
+                        if (mLauncher.getNavigationModel() == expectedMode) break;
+                        Thread.sleep(100);
+                    }
+                    Assert.assertTrue("Couldn't switch to " + overlayPackage,
+                            mLauncher.getNavigationModel() == expectedMode);
+
+                    for (int i = 0; i != 100; ++i) {
+                        if (mLauncher.getNavigationModeMismatchError() == null) break;
+                        Thread.sleep(100);
+                    }
+                    final String error = mLauncher.getNavigationModeMismatchError();
+                    Assert.assertTrue("Switching nav mode: " + error, error == null);
+
+                    Thread.sleep(5000);
+                }
+
+                private void setOverlayPackageEnabled(String overlayPackage, boolean enable)
+                        throws Exception {
+                    Log.d(TAG, "setOverlayPackageEnabled: " + overlayPackage + " " + enable);
+                    final String action = enable ? "enable" : "disable";
+                    UiDevice.getInstance(getInstrumentation()).executeShellCommand(
+                            "cmd overlay " + action + " " + overlayPackage);
+                }
+            };
+        } else {
+            return base;
+        }
+    }
+
+    private static LauncherInstrumentation.NavigationModel currentSysUiNavigationMode() {
+        return LauncherInstrumentation.getNavigationModel(
+                SysUINavigationMode.getMode(
+                        getInstrumentation().
+                                getTargetContext()).
+                        resValue);
+    }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
new file mode 100644
index 0000000..2111e2c
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
@@ -0,0 +1,102 @@
+/*
+ * 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.RaceConditionTracker.enterEvt;
+import static com.android.launcher3.util.RaceConditionTracker.exitEvt;
+
+import android.content.Intent;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.util.RaceConditionReproducer;
+import com.android.quickstep.NavigationModeSwitchRule.Mode;
+import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
+import com.android.quickstep.inputconsumers.OtherActivityInputConsumer;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class StartLauncherViaGestureTests extends AbstractQuickStepTest {
+
+    static final int STRESS_REPEAT_COUNT = 10;
+
+    @Override
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        // Start an activity where the gestures start.
+        startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
+    }
+
+    private void runTest(String... eventSequence) {
+        final RaceConditionReproducer eventProcessor = new RaceConditionReproducer(eventSequence);
+
+        // Destroy Launcher activity.
+        closeLauncherActivity();
+
+        // The test action.
+        eventProcessor.startIteration();
+        mLauncher.pressHome();
+        eventProcessor.finishIteration();
+    }
+
+    @Test
+    @Ignore // Ignoring until race condition repro framework is changes for multi-process case.
+    @NavigationModeSwitch(mode = Mode.TWO_BUTTON)
+    public void testPressHome() {
+        runTest(enterEvt(Launcher.ON_CREATE_EVT),
+                exitEvt(Launcher.ON_CREATE_EVT),
+                enterEvt(OtherActivityInputConsumer.DOWN_EVT),
+                exitEvt(OtherActivityInputConsumer.DOWN_EVT));
+
+        runTest(enterEvt(OtherActivityInputConsumer.DOWN_EVT),
+                exitEvt(OtherActivityInputConsumer.DOWN_EVT),
+                enterEvt(Launcher.ON_CREATE_EVT),
+                exitEvt(Launcher.ON_CREATE_EVT));
+    }
+
+    @Test
+    @NavigationModeSwitch
+    public void testStressPressHome() {
+        for (int i = 0; i < STRESS_REPEAT_COUNT; ++i) {
+            // Destroy Launcher activity.
+            closeLauncherActivity();
+
+            // The test action.
+            mLauncher.pressHome();
+        }
+    }
+
+    @Test
+    @NavigationModeSwitch
+    public void testStressSwipeToOverview() {
+        for (int i = 0; i < STRESS_REPEAT_COUNT; ++i) {
+            // Destroy Launcher activity.
+            closeLauncherActivity();
+
+            // The test action.
+            mLauncher.getBackground().switchToOverview();
+        }
+    }
+}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
new file mode 100644
index 0000000..9e3bf2f
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -0,0 +1,241 @@
+/*
+ * 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.ui.TaplTestsLauncher3.getAppPackageName;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Intent;
+import android.os.RemoteException;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.Until;
+
+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.Overview;
+import com.android.launcher3.tapl.OverviewTask;
+import com.android.launcher3.tapl.TestHelpers;
+import com.android.launcher3.ui.TaplTestsLauncher3;
+import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
+import com.android.quickstep.views.RecentsView;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class TaplTestsQuickstep extends AbstractQuickStepTest {
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        TaplTestsLauncher3.initialize(this);
+    }
+
+    private void startTestApps() throws Exception {
+        startAppFast(getAppPackageName());
+        startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
+        startTestActivity(2);
+
+        executeOnLauncher(launcher -> assertTrue(
+                "Launcher activity is the top activity; expecting another activity to be the top "
+                        + "one",
+                isInBackground(launcher)));
+    }
+
+    @Test
+    @PortraitLandscape
+    @Ignore // Enable after b/131115533
+    public void testPressRecentAppsLauncherAndGetOverview() throws RemoteException {
+        mDevice.pressRecentApps();
+        waitForState("Launcher internal state didn't switch to Overview", LauncherState.OVERVIEW);
+
+        assertNotNull("getOverview() returned null", mLauncher.getOverview());
+    }
+
+    @Test
+    @NavigationModeSwitch
+    @PortraitLandscape
+    public void testWorkspaceSwitchToAllApps() {
+        assertNotNull("switchToAllApps() returned null",
+                mLauncher.getWorkspace().switchToAllApps());
+        assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
+    }
+
+    @Test
+    public void testAllAppsFromOverview() throws Exception {
+        // 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 {
+        startTestApps();
+        Overview overview = mLauncher.pressHome().switchToOverview();
+        assertTrue("Launcher internal state didn't switch to Overview",
+                isInState(LauncherState.OVERVIEW));
+        executeOnLauncher(
+                launcher -> assertTrue("Don't have at least 3 tasks", getTaskCount(launcher) >= 3));
+
+        // Test flinging forward and backward.
+        executeOnLauncher(launcher -> assertEquals("Current task in Overview is not 0",
+                0, getCurrentOverviewPage(launcher)));
+
+        overview.flingForward();
+        assertTrue("Launcher internal state is not Overview", isInState(LauncherState.OVERVIEW));
+        final Integer currentTaskAfterFlingForward = getFromLauncher(
+                launcher -> getCurrentOverviewPage(launcher));
+        executeOnLauncher(launcher -> assertTrue("Current task in Overview is still 0",
+                currentTaskAfterFlingForward > 0));
+
+        overview.flingBackward();
+        assertTrue("Launcher internal state is not Overview", isInState(LauncherState.OVERVIEW));
+        executeOnLauncher(launcher -> assertTrue("Flinging back in Overview did nothing",
+                getCurrentOverviewPage(launcher) < currentTaskAfterFlingForward));
+
+        // Test opening a task.
+        OverviewTask task = mLauncher.pressHome().switchToOverview().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(
+                By.pkg(getAppPackageName()).text("TestActivity2")),
+                DEFAULT_UI_TIMEOUT));
+        executeOnLauncher(launcher -> assertTrue(
+                "Launcher activity is the top activity; expecting another activity to be the top "
+                        + "one",
+                isInBackground(launcher)));
+
+        // Test dismissing a task.
+        overview = mLauncher.pressHome().switchToOverview();
+        assertTrue("Launcher internal state didn't switch to Overview",
+                isInState(LauncherState.OVERVIEW));
+        final Integer numTasks = getFromLauncher(launcher -> getTaskCount(launcher));
+        task = overview.getCurrentTask();
+        assertNotNull("overview.getCurrentTask() returned null (2)", task);
+        task.dismiss();
+        executeOnLauncher(
+                launcher -> assertEquals("Dismissing a task didn't remove 1 task from Overview",
+                        numTasks - 1, getTaskCount(launcher)));
+
+        if (!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);
+
+        // Test dismissing all tasks.
+        mLauncher.getWorkspace().switchToOverview().dismissAllTasks();
+        waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL);
+        executeOnLauncher(
+                launcher -> assertEquals("Still have tasks after dismissing all",
+                        0, getTaskCount(launcher)));
+    }
+
+    private int getCurrentOverviewPage(Launcher launcher) {
+        return launcher.<RecentsView>getOverviewPanel().getCurrentPage();
+    }
+
+    private int getTaskCount(Launcher launcher) {
+        return launcher.<RecentsView>getOverviewPanel().getTaskViewCount();
+    }
+
+    @Test
+    public void testAppIconLaunchFromAllAppsFromOverview() throws Exception {
+        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 {
+        assertNotNull("Workspace.switchToOverview() returned null",
+                mLauncher.pressHome().switchToOverview());
+        assertTrue("Launcher internal state didn't switch to Overview",
+                isInState(LauncherState.OVERVIEW));
+    }
+
+    @Test
+    @NavigationModeSwitch
+//    @PortraitLandscape
+    public void testBackground() throws Exception {
+        startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
+        final Background background = mLauncher.getBackground();
+        assertNotNull("Launcher.getBackground() returned null", background);
+        executeOnLauncher(launcher -> assertTrue(
+                "Launcher activity is the top activity; expecting another activity to be the top "
+                        + "one",
+                isInBackground(launcher)));
+
+        assertNotNull("Background.switchToOverview() returned null", background.switchToOverview());
+        assertTrue("Launcher internal state didn't switch to Overview",
+                isInState(LauncherState.OVERVIEW));
+    }
+
+    @Test
+    @PortraitLandscape
+    public void testAllAppsFromHome() throws Exception {
+        // Test opening all apps
+        assertNotNull("switchToAllApps() returned null",
+                mLauncher.getWorkspace().switchToAllApps());
+
+        TaplTestsLauncher3.runAllAppsTest(this, mLauncher.getAllApps());
+
+        // Testing pressHome.
+        assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
+        assertNotNull("pressHome returned null", mLauncher.pressHome());
+        assertTrue("Launcher internal state is not Home", isInState(LauncherState.NORMAL));
+        assertNotNull("getHome returned null", mLauncher.getWorkspace());
+    }
+}
diff --git a/res/drawable-hdpi/ic_allapps.png b/res/drawable-hdpi/ic_allapps.png
deleted file mode 100644
index 253755f..0000000
--- a/res/drawable-hdpi/ic_allapps.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_allapps_pressed.png b/res/drawable-hdpi/ic_allapps_pressed.png
deleted file mode 100644
index 1e644c5..0000000
--- a/res/drawable-hdpi/ic_allapps_pressed.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_allapps.png b/res/drawable-mdpi/ic_allapps.png
deleted file mode 100644
index 6936b20..0000000
--- a/res/drawable-mdpi/ic_allapps.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_allapps_pressed.png b/res/drawable-mdpi/ic_allapps_pressed.png
deleted file mode 100644
index 850ded6..0000000
--- a/res/drawable-mdpi/ic_allapps_pressed.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-v26/adaptive_icon_drawable_wrapper.xml b/res/drawable-v26/adaptive_icon_drawable_wrapper.xml
deleted file mode 100644
index 2d78b69..0000000
--- a/res/drawable-v26/adaptive_icon_drawable_wrapper.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.
--->
-<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
-    <background android:drawable="@color/legacy_icon_background"/>
-    <foreground>
-        <com.android.launcher3.graphics.FixedScaleDrawable />
-    </foreground>
-</adaptive-icon>
diff --git a/res/drawable-v26/ic_deepshortcut_placeholder.xml b/res/drawable-v26/ic_deepshortcut_placeholder.xml
new file mode 100644
index 0000000..3fa8506
--- /dev/null
+++ b/res/drawable-v26/ic_deepshortcut_placeholder.xml
@@ -0,0 +1,20 @@
+<?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="?attr/popupColorSecondary"/>
+    <foreground android:drawable="?attr/popupColorSecondary"/>
+</adaptive-icon>
diff --git a/res/drawable-v28/round_rect_folder.xml b/res/drawable-v28/round_rect_folder.xml
new file mode 100644
index 0000000..0403be0
--- /dev/null
+++ b/res/drawable-v28/round_rect_folder.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <solid android:color="?attr/folderFillColor" />
+    <corners android:radius="?android:attr/dialogCornerRadius" />
+</shape>
diff --git a/res/drawable-v28/round_rect_primary.xml b/res/drawable-v28/round_rect_primary.xml
new file mode 100644
index 0000000..53679ed
--- /dev/null
+++ b/res/drawable-v28/round_rect_primary.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <solid android:color="?android:attr/colorPrimary" />
+    <corners android:radius="?android:attr/dialogCornerRadius" />
+</shape>
diff --git a/res/drawable-v28/top_round_rect_primary.xml b/res/drawable-v28/top_round_rect_primary.xml
new file mode 100644
index 0000000..5c40df7
--- /dev/null
+++ b/res/drawable-v28/top_round_rect_primary.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <solid android:color="?android:attr/colorPrimary" />
+    <corners
+        android:topLeftRadius="?android:attr/dialogCornerRadius"
+        android:topRightRadius="?android:attr/dialogCornerRadius"
+        android:bottomLeftRadius="0dp"
+        android:bottomRightRadius="0dp"
+        />
+</shape>
diff --git a/res/drawable-xhdpi/ic_allapps.png b/res/drawable-xhdpi/ic_allapps.png
deleted file mode 100644
index c11c103..0000000
--- a/res/drawable-xhdpi/ic_allapps.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_allapps_pressed.png b/res/drawable-xhdpi/ic_allapps_pressed.png
deleted file mode 100644
index f319bf1..0000000
--- a/res/drawable-xhdpi/ic_allapps_pressed.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_allapps.png b/res/drawable-xxhdpi/ic_allapps.png
deleted file mode 100644
index cf6a2cb..0000000
--- a/res/drawable-xxhdpi/ic_allapps.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_allapps_pressed.png b/res/drawable-xxhdpi/ic_allapps_pressed.png
deleted file mode 100644
index 379389a..0000000
--- a/res/drawable-xxhdpi/ic_allapps_pressed.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/all_apps_button_icon.xml b/res/drawable/all_apps_button_icon.xml
deleted file mode 100644
index 7c69cad..0000000
--- a/res/drawable/all_apps_button_icon.xml
+++ /dev/null
@@ -1,21 +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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_focused="true" android:drawable="@drawable/ic_allapps_pressed" />
-    <item android:state_pressed="true" android:drawable="@drawable/ic_allapps_pressed" />
-    <item android:drawable="@drawable/ic_allapps" />
-</selector>
diff --git a/res/drawable/deep_shortcuts_drag_handle.xml b/res/drawable/deep_shortcuts_drag_handle.xml
deleted file mode 100644
index 8fc3779..0000000
--- a/res/drawable/deep_shortcuts_drag_handle.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?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.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT 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/deep_shortcut_drag_handle_size"
-        android:height="@dimen/deep_shortcut_drag_handle_size"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"
-        android:tint="?android:attr/textColorHint" >
-
-    <path
-        android:pathData="M19,9H5c-0.55,0-1,0.45-1,1l0,0c0,0.55,0.45,1,1,1h14c0.55,0,1-0.45,1-1l0,0C20,9.45,19.55,9,19,9z M5,
-        15h14c0.55,0,1-0.45,1-1l0,0c0-0.55-0.45-1-1-1H5c-0.55,0-1,0.45-1,1l0,0C4,14.55,4.45,15,5,15z"
-        android:fillColor="@android:color/white" />
-</vector>
\ No newline at end of file
diff --git a/res/drawable/deep_shortcuts_text_placeholder.xml b/res/drawable/deep_shortcuts_text_placeholder.xml
new file mode 100644
index 0000000..99da50f
--- /dev/null
+++ b/res/drawable/deep_shortcuts_text_placeholder.xml
@@ -0,0 +1,21 @@
+<?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.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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="?attr/popupColorSecondary" />
+    <corners android:radius="8dp" />
+    <size android:height="16dp" />
+</shape>
diff --git a/res/drawable/ic_close.xml b/res/drawable/ic_close.xml
deleted file mode 100644
index 8b2f55f..0000000
--- a/res/drawable/ic_close.xml
+++ /dev/null
@@ -1,23 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportHeight="24.0"
-    android:viewportWidth="24.0">
-  <path
-      android:fillColor="@android:color/white"
-      android:pathData="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41z" />
-</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_corp.xml b/res/drawable/ic_corp.xml
index 48f5007..b59113d 100644
--- a/res/drawable/ic_corp.xml
+++ b/res/drawable/ic_corp.xml
@@ -17,8 +17,11 @@
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24.0"
-    android:viewportHeight="24.0">
+    android:viewportHeight="24.0"
+    android:tint="?android:attr/textColorHint" >
     <path
-        android:pathData="M20,6h-4V4c0,-1.11 -0.89,-2 -2,-2h-4C8.89,2 8,2.89 8,4v2H4C2.89,6 2.01,6.89 2.01,8L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V8C22,6.89 21.11,6 20,6zM12,15c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2s2,0.9 2,2S13.1,15 12,15zM14,6h-4V4h4V6z"
-        android:fillColor="?android:attr/textColorHint"/>
+        android:pathData="M20 6h-4V4c0-1.11-0.89-2-2-2h-4c-1.11 0-2 0.89-2 2v2H4c-1.11 0-1.99 0.89 -1.99
+2L2 19c0 1.11 0.89 2 2 2h16c1.11 0 2-0.89 2-2V8c0-1.11-0.89-2-2-2zM10
+4h4v2h-4V4zm10 15H4V8h16v11z"
+        android:fillColor="@android:color/white"/>
 </vector>
\ No newline at end of file
diff --git a/res/drawable/ic_deepshortcut_placeholder.xml b/res/drawable/ic_deepshortcut_placeholder.xml
new file mode 100644
index 0000000..85a9694
--- /dev/null
+++ b/res/drawable/ic_deepshortcut_placeholder.xml
@@ -0,0 +1,22 @@
+<?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.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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="oval">
+    <solid android:color="?attr/popupColorSecondary" />
+    <size
+        android:height="32dp"
+        android:width="32dp" />
+</shape>
diff --git a/res/drawable/ic_drag_handle.xml b/res/drawable/ic_drag_handle.xml
new file mode 100644
index 0000000..0181ff1
--- /dev/null
+++ b/res/drawable/ic_drag_handle.xml
@@ -0,0 +1,27 @@
+<?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.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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/deep_shortcut_drag_handle_size"
+        android:height="@dimen/deep_shortcut_drag_handle_size"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/textColorHint" >
+
+    <path
+        android:pathData="M20,9H4v2h16V9z M4,15h16v-2H4V15z"
+        android:fillColor="@android:color/white" />
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_info_no_shadow.xml b/res/drawable/ic_info_no_shadow.xml
index d816f12..7c43779 100644
--- a/res/drawable/ic_info_no_shadow.xml
+++ b/res/drawable/ic_info_no_shadow.xml
@@ -21,12 +21,12 @@
         android:tint="?android:attr/textColorPrimary">
 
     <path
-        android:fillColor="#FFFFFF"
+        android:fillColor="@android:color/white"
         android:pathData="M 11 7 H 13 V 9 H 11 V 7 Z" />
     <path
-        android:fillColor="#FFFFFF"
+        android:fillColor="@android:color/white"
         android:pathData="M 11 11 H 13 V 17 H 11 V 11 Z" />
     <path
-        android:fillColor="#FFFFFF"
+        android:fillColor="@android:color/white"
         android:pathData="M12,2C6.48,2,2,6.48,2,12c0,5.52,4.48,10,10,10s10-4.48,10-10C22,6.48,17.52,2,12,2z M12,20c-4.41,0-8-3.59-8-8 c0-4.41,3.59-8,8-8s8,3.59,8,8C20,16.41,16.41,20,12,20z" />
 </vector>
diff --git a/res/drawable/ic_install_no_shadow.xml b/res/drawable/ic_install_no_shadow.xml
index ffce22a..eaad0de 100644
--- a/res/drawable/ic_install_no_shadow.xml
+++ b/res/drawable/ic_install_no_shadow.xml
@@ -17,11 +17,13 @@
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24"
-    android:viewportHeight="24">
+    android:viewportHeight="24"
+    android:tint="?android:attr/textColorPrimary">
 
     <path
-        android:fillColor="?android:attr/textColorPrimary"
-        android:pathData="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z" />
+        android:fillColor="@android:color/white"
+        android:pathData="M18,15v3H6v-3H4v3c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2v-3H18z" />
     <path
-        android:pathData="M0 0h24v24H0z" />
+        android:fillColor="@android:color/white"
+        android:pathData="M 17 11.5 L 15.59 10.09 L 13 12.67 L 13 4 L 11 4 L 11 12.67 L 8.41 10.09 L 7 11.5 L 12 16.5 Z" />
 </vector>
diff --git a/res/drawable/ic_instant_app_badge.xml b/res/drawable/ic_instant_app_badge.xml
deleted file mode 100644
index cc53230..0000000
--- a/res/drawable/ic_instant_app_badge.xml
+++ /dev/null
@@ -1,43 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="@dimen/profile_badge_size"
-    android:height="@dimen/profile_badge_size"
-    android:viewportWidth="18"
-    android:viewportHeight="18">
-
-    <path
-        android:fillColor="@android:color/black"
-        android:fillType="evenOdd"
-        android:strokeWidth="1"
-        android:pathData="M 9 0 C 13.9705627485 0 18 4.02943725152 18 9 C 18 13.9705627485 13.9705627485 18 9 18 C 4.02943725152 18 0 13.9705627485 0 9 C 0 4.02943725152 4.02943725152 0 9 0 Z" />
-    <path
-        android:fillColor="@android:color/white"
-        android:fillType="evenOdd"
-        android:strokeWidth="1"
-        android:pathData="M 9 0 C 13.9705627485 0 18 4.02943725152 18 9 C 18 13.9705627485 13.9705627485 18 9 18 C 4.02943725152 18 0 13.9705627485 0 9 C 0 4.02943725152 4.02943725152 0 9 0 Z" />
-    <path
-        android:fillColor="@android:color/white"
-        android:fillType="evenOdd"
-        android:strokeWidth="1"
-        android:pathData="M 9 0 C 13.9705627485 0 18 4.02943725152 18 9 C 18 13.9705627485 13.9705627485 18 9 18 C 4.02943725152 18 0 13.9705627485 0 9 C 0 4.02943725152 4.02943725152 0 9 0 Z" />
-    <path
-        android:fillColor="@android:color/black"
-        android:fillAlpha="0.87"
-        android:fillType="evenOdd"
-        android:strokeWidth="1"
-        android:pathData="M 6 10.4123279 L 8.63934949 10.4123279 L 8.63934949 15.6 L 12.5577168 7.84517705 L 9.94547194 7.84517705 L 9.94547194 2 Z" />
-</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_palette.xml b/res/drawable/ic_palette.xml
new file mode 100644
index 0000000..21cec2d
--- /dev/null
+++ b/res/drawable/ic_palette.xml
@@ -0,0 +1,41 @@
+<?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.
+-->
+<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="@android:color/white"
+        android:strokeWidth="1"
+        android:pathData="M10,20 C4.49,20 0,15.51 0,10 C0,4.49 4.49,0 10,0 C15.51,0 20,4.04 20,9
+            C20,12.31 17.31,15 14,15 L12.23,15 C11.95,15 11.73,15.22 11.73,15.5 C11.73,15.62
+            11.78,15.73 11.86,15.83 C12.27,16.3 12.5,16.89 12.5,17.5 C12.5,18.88 11.38,20
+            10,20 Z M10,2 C5.59,2 2,5.59 2,10 C2,14.41 5.59,18 10,18 C10.28,18 10.5,17.78
+            10.5,17.5 C10.5,17.34 10.42,17.22 10.36,17.15 C9.95,16.69 9.73,16.1 9.73,15.5
+            C9.73,14.12 10.85,13 12.23,13 L14,13 C16.21,13 18,11.21 18,9 C18,5.14 14.41,2
+            10,2 Z M4.5,11 C5.32842712,11 6,10.3284271 6,9.5 C6,8.67157288 5.32842712,8
+            4.5,8 C3.67157288,8 3,8.67157288 3,9.5 C3,10.3284271 3.67157288,11 4.5,11 Z
+            M7.5,7 C8.32842712,7 9,6.32842712 9,5.5 C9,4.67157288 8.32842712,4 7.5,4
+            C6.67157288,4 6,4.67157288 6,5.5 C6,6.32842712 6.67157288,7 7.5,7 Z M12.5,7
+            C13.3284271,7 14,6.32842712 14,5.5 C14,4.67157288 13.3284271,4 12.5,4
+            C11.6715729,4 11,4.67157288 11,5.5 C11,6.32842712 11.6715729,7 12.5,7 Z M15.5,11
+            C16.3284271,11 17,10.3284271 17,9.5 C17,8.67157288 16.3284271,8 15.5,8
+            C14.6715729,8 14,8.67157288 14,9.5 C14,10.3284271 14.6715729,11 15.5,11 Z" />
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_remove_no_shadow.xml b/res/drawable/ic_remove_no_shadow.xml
index be7f9f3..2c706db 100644
--- a/res/drawable/ic_remove_no_shadow.xml
+++ b/res/drawable/ic_remove_no_shadow.xml
@@ -14,14 +14,12 @@
      limitations under the License.
 -->
 <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/textColorPrimary" >
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0"
+    android:tint="?android:attr/textColorPrimary">
     <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M13.41,12l5.29-5.29c0.39-0.39,0.39-1.02,0-1.41c-0.39-0.39-1.02-0.39-1.41,0L12,10.59L6.71,
-        5.29c-0.39-0.39-1.02-0.39-1.41,0c-0.39,0.39-0.39,1.02,0,1.41L10.59,12l-5.29,5.29c-0.39,0.39-0.39,1.02,
-        0,1.41c0.39,0.39,1.02,0.39,1.41,0L12,13.41l5.29,5.29c0.39,0.39,1.02,0.39,1.41,0c0.39-0.39,0.39-1.02,0-1.41L13.41,12z"/>
+        android:fillColor="@android:color/white"
+        android:pathData="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41z" />
 </vector>
diff --git a/res/drawable/ic_setting.xml b/res/drawable/ic_setting.xml
index a83aab3..8a3728c 100644
--- a/res/drawable/ic_setting.xml
+++ b/res/drawable/ic_setting.xml
@@ -20,7 +20,7 @@
         android:viewportHeight="24.0"
         android:tint="?android:attr/textColorPrimary" >
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="@android:color/white"
         android:pathData="M13.85,22.25h-3.7c-0.74,0-1.36-0.54-1.45-1.27l-0.27-1.89c-0.27-0.14-0.53-0.29-0.79-0.46l-1.8,0.72
             c-0.7,0.26-1.47-0.03-1.81-0.65L2.2,15.53c-0.35-0.66-0.2-1.44,0.36-1.88l1.53-1.19c-0.01-0.15-0.02-0.3-0.02-0.46
             c0-0.15,0.01-0.31,0.02-0.46l-1.52-1.19C1.98,9.9,1.83,9.09,2.2,8.47l1.85-3.19c0.34-0.62,1.11-0.9,1.79-0.63l1.81,0.73
@@ -38,6 +38,6 @@
              M18.22,6.27c0,0.01,0.01,0.02,0.01,0.02L18.22,6.27z M5.79,6.25L5.78,6.27C5.78,6.27,5.79,6.26,5.79,6.25z M13.31,3.28
             c0,0.01,0,0.01,0,0.02L13.31,3.28z M10.69,3.26l0,0.02C10.69,3.27,10.69,3.27,10.69,3.26z"/>
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="@android:color/white"
         android:pathData="M8.5,12a3.5,3.5 0 1,0 7,0a3.5,3.5 0 1,0 -7,0"/>
 </vector>
diff --git a/res/drawable/ic_uninstall_no_shadow.xml b/res/drawable/ic_uninstall_no_shadow.xml
index 37632d1..6aff102 100644
--- a/res/drawable/ic_uninstall_no_shadow.xml
+++ b/res/drawable/ic_uninstall_no_shadow.xml
@@ -20,12 +20,12 @@
         android:viewportHeight="24.0"
         android:tint="?android:attr/textColorPrimary" >
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="@android:color/white"
         android:pathData="M15,4V3H9v1H4v2h1v13c0,1.1,0.9,2,2,2h10c1.1,0,2-0.9,2-2V6h1V4H15z M17,19H7V6h10V19z" />
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="@android:color/white"
         android:pathData="M 9 8 H 11 V 17 H 9 V 8 Z" />
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="@android:color/white"
         android:pathData="M 13 8 H 15 V 17 H 13 V 8 Z" />
 </vector>
diff --git a/res/drawable/ic_wallpaper.xml b/res/drawable/ic_wallpaper.xml
index 7fd9340..9543f88 100644
--- a/res/drawable/ic_wallpaper.xml
+++ b/res/drawable/ic_wallpaper.xml
@@ -1,5 +1,5 @@
 <!--
-Copyright (C) 2016 The Android Open Source Project
+   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.
@@ -17,9 +17,10 @@
         android:width="@dimen/options_menu_icon_size"
         android:height="@dimen/options_menu_icon_size"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/textColorPrimary">
     <path
-        android:fillColor="?android:attr/textColorPrimary"
+        android:fillColor="@android:color/white"
         android:pathData="M9,12.71l2.14,2.58l3-3.87L18,16.57H6L9,12.71z M5,5h6V3H5C3.9,3,3,3.9,3,5v6h2V5z M19,19h-6v2h6c1.1,0,2-0.9,2-2v-6h-2V19z
             M5,19v-6H3v6c0,1.1,0.9,2,2,2h6v-2H5z M19,5v6h2V5c0-1.1-0.9-2-2-2h-6v2H19z M16,9c0.55,0,1-0.45,1-1s-0.45-1-1-1
             c-0.55,0-1,0.45-1,1S15.45,9,16,9z"/>
diff --git a/res/drawable/ic_warning.xml b/res/drawable/ic_warning.xml
index 332563d..c2c6e98 100644
--- a/res/drawable/ic_warning.xml
+++ b/res/drawable/ic_warning.xml
@@ -20,8 +20,12 @@
         android:viewportHeight="24.0"
         android:tint="?android:attr/textColorPrimary" >
     <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M22.85,19.39L12.93,2.25c-0.41-0.71-1.44-0.71-1.85,0L1.15,19.39C0.73,20.11,1.25,21,2.07,21h19.85
-        C22.75,21,23.27,20.11,22.85,19.39z M11,10c0-0.55,0.45-1,1-1s1,0.45,1,1v3c0,0.55-0.45,1-1,1s-1-0.45-1-1V10z M12,18.2
-        c-0.61,0-1.1-0.49-1.1-1.1S11.39,16,12,16s1.1,0.49,1.1,1.1C13.1,17.71,12.61,18.2,12,18.2z"/>
+        android:fillColor="@android:color/white"
+        android:pathData="M12,5.99L19.53,19H4.47L12,5.99 M12,2L1,21h22L12,2L12,2z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M 13 16 L 11 16 L 11 18 L 13 18 L 13 16 Z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M 13 10 L 11 10 L 11 14 L 13 14 L 13 10 Z" />
 </vector>
diff --git a/res/drawable/ic_widget.xml b/res/drawable/ic_widget.xml
index 3ebbb68..460fe94 100644
--- a/res/drawable/ic_widget.xml
+++ b/res/drawable/ic_widget.xml
@@ -17,9 +17,10 @@
         android:width="@dimen/options_menu_icon_size"
         android:height="@dimen/options_menu_icon_size"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/textColorPrimary">
     <path
-        android:fillColor="?android:attr/textColorPrimary"
+        android:fillColor="@android:color/white"
         android:pathData="M16.66,4.52l2.83,2.83l-2.83,2.83l-2.83-2.83L16.66,4.52 M9,5v4H5V5H9 M19,15v4h-4v-4H19 M9,15v4H5v-4H9 M16.66,1.69
             L11,7.34L16.66,13l5.66-5.66L16.66,1.69L16.66,1.69z M11,3H3v8h8V7.34V3L11,3z M21,13h-4.34H13v8h8V13L21,13z M11,13H3v8h8V13L11,13z"/>
 </vector>
diff --git a/res/drawable/qsb_host_view_focus_bg.xml b/res/drawable/qsb_host_view_focus_bg.xml
new file mode 100644
index 0000000..7300b6a
--- /dev/null
+++ b/res/drawable/qsb_host_view_focus_bg.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- Used as the widget host view background when giving focus to a child via keyboard. -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_selected="true">
+        <shape android:shape="rectangle">
+            <stroke android:color="#fff" android:width="2dp" />
+        </shape>
+    </item>
+    <item android:state_focused="true">
+        <shape android:shape="rectangle">
+            <solid android:color="@color/focused_background" />
+        </shape>
+    </item>
+</selector>
\ No newline at end of file
diff --git a/res/drawable/round_rect_folder.xml b/res/drawable/round_rect_folder.xml
new file mode 100644
index 0000000..8b3d06c
--- /dev/null
+++ b/res/drawable/round_rect_folder.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <solid android:color="?attr/folderFillColor" />
+    <corners android:radius="@dimen/bg_round_rect_radius" />
+</shape>
diff --git a/res/layout/add_item_confirmation_activity.xml b/res/layout/add_item_confirmation_activity.xml
index 6c316e6..830255b 100644
--- a/res/layout/add_item_confirmation_activity.xml
+++ b/res/layout/add_item_confirmation_activity.xml
@@ -44,7 +44,7 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:background="?android:attr/colorPrimaryDark"
-                android:theme="@style/WidgetContainerTheme">
+                android:theme="?attr/widgetsTheme">
 
                 <com.android.launcher3.dragndrop.LivePreviewWidgetCell
                     android:id="@+id/widget_cell"
diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml
index 02d793e..9427ae0 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -24,11 +24,55 @@
     android:clipChildren="true"
     android:clipToPadding="false"
     android:focusable="false"
-    android:saveEnabled="false" >
+    android:saveEnabled="false">
 
-    <include layout="@layout/all_apps_rv_layout" />
+    <include
+        layout="@layout/all_apps_rv_layout"
+        android:visibility="gone" />
 
-    <include layout="@layout/all_apps_floating_header" />
+    <com.android.launcher3.allapps.FloatingHeaderView
+        android:id="@+id/all_apps_header"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/search_container_all_apps"
+        android:clipToPadding="false"
+        android:paddingTop="@dimen/all_apps_header_top_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:textAllCaps="true"
+                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:textAllCaps="true"
+                android:textColor="@color/all_apps_tab_text"
+                android:textSize="14sp" />
+        </com.android.launcher3.allapps.PersonalWorkSlidingTabStrip>
+    </com.android.launcher3.allapps.FloatingHeaderView>
 
     <include
         android:id="@id/search_container_all_apps"
diff --git a/res/layout/all_apps_button.xml b/res/layout/all_apps_button.xml
deleted file mode 100644
index 4bc780a..0000000
--- a/res/layout/all_apps_button.xml
+++ /dev/null
@@ -1,17 +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.
--->
-
-<TextView style="@style/BaseIcon" />
diff --git a/res/layout/all_apps_floating_header.xml b/res/layout/all_apps_floating_header.xml
deleted file mode 100644
index c4240f8..0000000
--- a/res/layout/all_apps_floating_header.xml
+++ /dev/null
@@ -1,58 +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.
--->
-<com.android.launcher3.allapps.FloatingHeaderView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/all_apps_header"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_below="@id/search_container_all_apps"
-    android:clipToPadding="false"
-    android:paddingTop="@dimen/all_apps_header_top_padding"
-    android:orientation="vertical" >
-
-    <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">
-
-        <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:fontFamily="sans-serif-medium"
-            android:text="@string/all_apps_personal_tab"
-            android:textAllCaps="true"
-            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:fontFamily="sans-serif-medium"
-            android:text="@string/all_apps_work_tab"
-            android:textAllCaps="true"
-            android:textColor="@color/all_apps_tab_text"
-            android:textSize="14sp" />
-    </com.android.launcher3.allapps.PersonalWorkSlidingTabStrip>
-</com.android.launcher3.allapps.FloatingHeaderView>
diff --git a/res/layout/deep_shortcut.xml b/res/layout/deep_shortcut.xml
index 4a2ad42..840a8b7 100644
--- a/res/layout/deep_shortcut.xml
+++ b/res/layout/deep_shortcut.xml
@@ -29,11 +29,10 @@
         android:textAlignment="viewStart"
         android:paddingStart="@dimen/deep_shortcuts_text_padding_start"
         android:paddingEnd="@dimen/popup_padding_end"
-        android:drawableEnd="@drawable/deep_shortcuts_drag_handle"
+        android:drawableEnd="@drawable/ic_drag_handle"
         android:drawablePadding="@dimen/deep_shortcut_drawable_padding"
         android:textSize="14sp"
         android:textColor="?android:attr/textColorPrimary"
-        android:fontFamily="sans-serif"
         launcher:layoutHorizontal="true"
         launcher:iconDisplay="shortcut_popup"
         launcher:iconSizeOverride="@dimen/deep_shortcut_icon_size" />
@@ -43,7 +42,8 @@
         android:layout_width="@dimen/deep_shortcut_icon_size"
         android:layout_height="@dimen/deep_shortcut_icon_size"
         android:layout_marginStart="@dimen/popup_padding_start"
-        android:layout_gravity="start|center_vertical" />
+        android:layout_gravity="start|center_vertical"
+        android:background="@drawable/ic_deepshortcut_placeholder"/>
 
     <View
         android:id="@+id/divider"
diff --git a/res/layout/floating_header_content.xml b/res/layout/floating_header_content.xml
new file mode 100644
index 0000000..e4061c2
--- /dev/null
+++ b/res/layout/floating_header_content.xml
@@ -0,0 +1,16 @@
+<?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.
+-->
+<merge />
\ No newline at end of file
diff --git a/res/layout/floating_icon_view.xml b/res/layout/floating_icon_view.xml
new file mode 100644
index 0000000..240c486
--- /dev/null
+++ b/res/layout/floating_icon_view.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+<com.android.launcher3.views.FloatingIconView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content" />
diff --git a/res/layout/folder_application.xml b/res/layout/folder_application.xml
index de861a0..c156e11 100644
--- a/res/layout/folder_application.xml
+++ b/res/layout/folder_application.xml
@@ -18,5 +18,6 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:launcher="http://schemas.android.com/apk/res-auto"
     style="@style/BaseIcon"
+    android:textColor="?attr/folderTextColor"
     android:includeFontPadding="false"
     launcher:iconDisplay="folder" />
diff --git a/res/layout/hotseat.xml b/res/layout/hotseat.xml
index 00f0b5f..82b0b8d 100644
--- a/res/layout/hotseat.xml
+++ b/res/layout/hotseat.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
+<!-- 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.
@@ -14,15 +14,11 @@
      limitations under the License.
 -->
 <com.android.launcher3.Hotseat
-    android:theme="@style/HomeScreenElementTheme"
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:launcher="http://schemas.android.com/apk/res-auto">
-
-    <com.android.launcher3.CellLayout
-        android:id="@+id/layout"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:layout_gravity="center"
-        launcher:containerType="hotseat"
-        android:importantForAccessibility="no" />
-</com.android.launcher3.Hotseat>
+    xmlns:launcher="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/hotseat"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:theme="@style/HomeScreenElementTheme"
+    android:importantForAccessibility="no"
+    launcher:containerType="hotseat" />
\ No newline at end of file
diff --git a/res/layout/launcher.xml b/res/layout/launcher.xml
index 304d012..cca899b 100644
--- a/res/layout/launcher.xml
+++ b/res/layout/launcher.xml
@@ -38,6 +38,11 @@
             android:theme="@style/HomeScreenElementTheme"
             launcher:pageIndicator="@+id/page_indicator" />
 
+        <!-- DO NOT CHANGE THE ID -->
+        <include
+            android:id="@+id/hotseat"
+            layout="@layout/hotseat" />
+
         <include
             android:id="@+id/overview_panel"
             layout="@layout/overview_panel"
@@ -56,22 +61,16 @@
             android:id="@+id/drop_target_bar"
             layout="@layout/drop_target_bar" />
 
-        <include android:id="@+id/scrim_view"
+        <include
+            android:id="@+id/scrim_view"
             layout="@layout/scrim_view" />
 
         <include
             android:id="@+id/apps_view"
             layout="@layout/all_apps"
             android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:visibility="invisible" />
-
-        <!-- DO NOT CHANGE THE ID -->
-        <include
-            android:id="@+id/hotseat"
-            layout="@layout/hotseat"
-            android:layout_width="match_parent"
             android:layout_height="match_parent" />
+
     </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
new file mode 100644
index 0000000..3fd02e3
--- /dev/null
+++ b/res/layout/launcher_preview_layout.xml
@@ -0,0 +1,50 @@
+<?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.
+-->
+<com.android.launcher3.InsettableFrameLayout
+    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">
+
+    <com.android.launcher3.CellLayout
+        android:id="@+id/workspace"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_gravity="top|left"
+        android:theme="@style/HomeScreenElementTheme"
+        launcher:containerType="workspace"
+        launcher:pageIndicator="@+id/page_indicator"/>
+
+    <com.android.launcher3.Hotseat
+        android:id="@+id/hotseat"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:importantForAccessibility="no"
+        android:theme="@style/HomeScreenElementTheme"
+        launcher:containerType="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
diff --git a/res/layout/snackbar.xml b/res/layout/snackbar.xml
new file mode 100644
index 0000000..b818943
--- /dev/null
+++ b/res/layout/snackbar.xml
@@ -0,0 +1,47 @@
+<?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.
+-->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <TextView
+        android:id="@+id/label"
+        android:layout_height="@dimen/snackbar_content_height"
+        android:layout_width="0dp"
+        android:layout_weight="1"
+        android:gravity="center_vertical"
+        android:paddingLeft="8dp"
+        android:paddingRight="8dp"
+        android:lines="1"
+        android:ellipsize="end"
+        android:textSize="@dimen/snackbar_max_text_size"
+        android:textColor="?android:attr/textColorPrimary"
+        style="@style/TextTitle"/>
+    <TextView
+        android:id="@+id/action"
+        android:layout_height="@dimen/snackbar_content_height"
+        android:layout_width="wrap_content"
+        android:layout_weight="0"
+        android:gravity="center"
+        android:paddingLeft="8dp"
+        android:paddingRight="8dp"
+        android:background="?android:attr/selectableItemBackground"
+        android:textStyle="bold"
+        android:textSize="@dimen/snackbar_max_text_size"
+        android:textColor="?android:attr/colorAccent"
+        style="@style/TextTitle"
+        android:capitalize="sentences"/>
+</merge>
\ No newline at end of file
diff --git a/res/layout/switch_preference_with_settings.xml b/res/layout/switch_preference_with_settings.xml
new file mode 100644
index 0000000..d319561
--- /dev/null
+++ b/res/layout/switch_preference_with_settings.xml
@@ -0,0 +1,46 @@
+<?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.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal"
+    android:gravity="center_vertical">
+
+    <ImageView
+        android:id="@+id/settings"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:src="@drawable/ic_setting"
+        android:tint="@android:color/black"
+        android:padding="12dp"
+        android:background="?android:attr/selectableItemBackgroundBorderless" />
+
+    <View
+        android:id="@+id/divider"
+        android:layout_width="1dp"
+        android:layout_height="30dp"
+        android:layout_marginEnd="8dp"
+        android:background="?android:attr/listDivider" />
+
+    <!-- Note: seems we need focusable="false" and clickable="false" when moving to androidx -->
+    <Switch
+        android:id="@android:id/switch_widget"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:background="@null" />
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/system_shortcut.xml b/res/layout/system_shortcut.xml
index 04f3d02..4b7097a 100644
--- a/res/layout/system_shortcut.xml
+++ b/res/layout/system_shortcut.xml
@@ -31,7 +31,6 @@
         android:paddingEnd="@dimen/popup_padding_end"
         android:textSize="14sp"
         android:textColor="?android:attr/textColorPrimary"
-        android:fontFamily="sans-serif"
         launcher:iconDisplay="shortcut_popup"
         launcher:layoutHorizontal="true"
         android:focusable="false" />
diff --git a/res/layout/system_shortcut_icons.xml b/res/layout/system_shortcut_icons.xml
index 4daf469..a340f4f 100644
--- a/res/layout/system_shortcut_icons.xml
+++ b/res/layout/system_shortcut_icons.xml
@@ -22,4 +22,10 @@
     android:orientation="horizontal"
     android:gravity="end|center_vertical"
     android:background="?attr/popupColorSecondary"
-    android:clipToPadding="true" />
+    android:clipToPadding="true">
+
+    <Space android:layout_width="0dp"
+           android:layout_height="match_parent"
+           android:layout_weight="1"
+           android:id="@+id/separator"/>
+</LinearLayout>
diff --git a/res/layout/user_folder_icon_normalized.xml b/res/layout/user_folder_icon_normalized.xml
index 2e6ce94..a1033f0 100644
--- a/res/layout/user_folder_icon_normalized.xml
+++ b/res/layout/user_folder_icon_normalized.xml
@@ -18,7 +18,7 @@
     xmlns:launcher="http://schemas.android.com/apk/res-auto"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:background="@drawable/round_rect_primary"
+    android:background="@drawable/round_rect_folder"
     android:elevation="5dp"
     android:orientation="vertical" >
 
@@ -56,9 +56,9 @@
             android:paddingBottom="@dimen/folder_label_padding_bottom"
             android:paddingTop="@dimen/folder_label_padding_top"
             android:singleLine="true"
-            android:textColor="?android:attr/textColorTertiary"
+            android:textColor="?attr/folderTextColor"
             android:textColorHighlight="?android:attr/colorControlHighlight"
-            android:textColorHint="?android:attr/textColorHint"
+            android:textColorHint="?attr/folderTextColor"
             android:textSize="@dimen/folder_label_text_size" />
 
         <com.android.launcher3.pageindicators.PageIndicatorDots
diff --git a/res/layout/widget_cell_content.xml b/res/layout/widget_cell_content.xml
index c77b0b9..64f2362 100644
--- a/res/layout/widget_cell_content.xml
+++ b/res/layout/widget_cell_content.xml
@@ -34,7 +34,6 @@
             android:layout_weight="1"
             android:ellipsize="end"
             android:fadingEdge="horizontal"
-            android:fontFamily="sans-serif-condensed"
             android:gravity="start"
             android:singleLine="true"
             android:maxLines="1"
@@ -51,7 +50,6 @@
             android:layout_marginLeft="5dp"
             android:textColor="?android:attr/textColorSecondary"
             android:textSize="14sp"
-            android:fontFamily="sans-serif-condensed"
             android:alpha="0.8" />
     </LinearLayout>
 
diff --git a/res/layout/widgets_bottom_sheet.xml b/res/layout/widgets_bottom_sheet.xml
index 6bf9048..3fdfc96 100644
--- a/res/layout/widgets_bottom_sheet.xml
+++ b/res/layout/widgets_bottom_sheet.xml
@@ -26,7 +26,7 @@
     android:theme="?attr/widgetsTheme">
 
     <TextView
-        style="@style/TextTitle"
+        style="@style/TextHeadline"
         android:id="@+id/title"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
diff --git a/res/layout/widgets_scroll_container.xml b/res/layout/widgets_scroll_container.xml
index 33c981a..fc509d1 100644
--- a/res/layout/widgets_scroll_container.xml
+++ b/res/layout/widgets_scroll_container.xml
@@ -23,6 +23,7 @@
     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"
diff --git a/res/layout/work_tab_bottom_user_education_view.xml b/res/layout/work_tab_bottom_user_education_view.xml
index ba6a939..ac2deeb 100644
--- a/res/layout/work_tab_bottom_user_education_view.xml
+++ b/res/layout/work_tab_bottom_user_education_view.xml
@@ -44,7 +44,7 @@
         android:layout_marginEnd="12dp"
         android:layout_gravity="right"
         android:contentDescription="@string/bottom_work_tab_user_education_close_button"
-        android:src="@drawable/ic_close"/>
+        android:src="@drawable/ic_remove_no_shadow"/>
 
     <TextView
         android:layout_width="wrap_content"
diff --git a/res/raw/downgrade_schema.json b/res/raw/downgrade_schema.json
index 3c1b64f..8f1780e 100644
--- a/res/raw/downgrade_schema.json
+++ b/res/raw/downgrade_schema.json
@@ -2,8 +2,12 @@
   // Note: Comments are not supported in JSON schema, but android parser is lenient.
 
   // Maximum DB version supported by this schema
-  "version" : 27,
+  "version" : 28,
 
+  "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"
+  ],
   // Downgrade from 27 to 26. Empty array indicates, the DB is compatible
   "downgrade_to_26" : [],
   "downgrade_to_25" : [],
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 35539e3..ca59afa 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,9 +65,9 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Dit is \'n stelselprogram en kan nie gedeïnstalleer word nie."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Naamlose vouer"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Het <xliff:g id="APP_NAME">%1$s</xliff:g> gedeaktiveer"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> het <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> kennisgewings</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> het <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> kennisgewing</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, het <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> kennisgewings</item>
+      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, het <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> kennisgewing</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"Bladsy %1$d van %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Tuisskerm %1$d van %2$d"</string>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Vouer: <xliff:g id="NAME">%1$s</xliff:g>"</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>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Gedeaktiveer deur jou administrateur"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Laat toe dat tuisskerm gedraai word"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Wanneer foon gedraai word"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Kennisgewingkolle"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Aan"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Af"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Kennisgewingkolle"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Aan"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Af"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Kennisgewingtoegang word benodig"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Skakel programkennisgewings vir <xliff:g id="NAME">%1$s</xliff:g> aan om kennisgewingkolle te sien"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Verander instellings"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Wys kennisgewingkolle"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Wys kennisgewingkolle"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Voeg ikoon by tuisskerm"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Vir nuwe programme"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Verander ikoon se vorm"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"op tuisskerm"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Gebruik stelselverstek"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Vierkant"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Sirkelvierkant"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Sirkel"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Traandruppel"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Pas ikoonvormveranderings toe"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Onbekend"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Verwyder"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Soek"</string>
@@ -114,6 +108,7 @@
     <string name="action_move_here" msgid="2170188780612570250">"Skuif item hierheen"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Item is by tuisskerm gevoeg"</string>
     <string name="item_removed" msgid="851119963877842327">"Item is verwyder"</string>
+    <string name="undo" msgid="4151576204245173321">"Ontdoen"</string>
     <string name="action_move" msgid="4339390619886385032">"Skuif item"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Skuif na ry <xliff:g id="NUMBER_0">%1$s</xliff:g> kolom <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Skuif na posisie <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Kennisgewings en programme is af"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Maak toe"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Toe"</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 921fbd9..d942e9e 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,9 +65,9 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"ይህ የስርዓት መተግበሪያ ነው እና ማራገፍ አይቻልም።"</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"ስም-አልባ አቃፊ"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> ተሰናክሏል"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>፣ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ማሳወቂያዎች አለው</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>፣ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ማሳወቂያዎች አለው</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>፣ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ማሳወቂያ አለው</item>
+      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>፣ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ማሳወቂያ አለው</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"ገጽ %1$d ከ%2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"መነሻ ማያ ገጽ %1$d ከ%2$d"</string>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"አቃፊ፦ <xliff:g id="NAME">%1$s</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="icon_badging_title" msgid="874121399231955394">"የማሳወቂያ ነጥቦች"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"በርቷል"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"ጠፍቷል"</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="icon_badging_service_title" msgid="2309733118428242174">"የማሳወቂያ ነጥቦችን አሳይ"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"የማሳወቂያ ነጥቦችን አሳይ"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"አዶ ወደ የመነሻ ማያ ገጽ አክል"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"ለአዲስ መተግበሪያዎች"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"የአዶ ቅርፅ ለውጥ"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"በመነሻ ማያ ገጽ ላይ"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"የሥርዓቱን ነባሪ ተጠቀም"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"ካሬ"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Squircle"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"ክብ"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"የእንባ ጠብታ"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"የአዶ ቅርች ለውጦች"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"የማይታወቅ"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"አስወግድ"</string>
     <string name="abandoned_search" msgid="891119232568284442">"ፈልግ"</string>
@@ -114,6 +108,7 @@
     <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>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"ማሳወቂያዎች እና መተግበሪያዎች ጠፍተዋል"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"ዝጋ"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"ዝግ"</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 ecf32d4..a80ecb0 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,13 +65,13 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"هذا تطبيق نظام وتتعذر إزالته."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"مجلد بدون اسم"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"تم إيقاف <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="zero"><xliff:g id="APP_NAME_2">%1$s</xliff:g>، به <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> إشعار</item>
-      <item quantity="two"><xliff:g id="APP_NAME_2">%1$s</xliff:g>، به إشعاران (<xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>)</item>
-      <item quantity="few"><xliff:g id="APP_NAME_2">%1$s</xliff:g>، به <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> إشعارات</item>
-      <item quantity="many"><xliff:g id="APP_NAME_2">%1$s</xliff:g>، به <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> إشعارًا</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>، به <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> إشعار</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>، به <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> إشعار</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="zero">يتضمن تطبيق <xliff:g id="APP_NAME_2">%1$s</xliff:g> <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> إشعار.​</item>
+      <item quantity="two">يتضمن تطبيق <xliff:g id="APP_NAME_2">%1$s</xliff:g> إشعارين (<xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>).</item>
+      <item quantity="few">يتضمن تطبيق <xliff:g id="APP_NAME_2">%1$s</xliff:g> <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> إشعارات.​</item>
+      <item quantity="many">يتضمن تطبيق <xliff:g id="APP_NAME_2">%1$s</xliff:g> <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> إشعارًا.</item>
+      <item quantity="other">يتضمن تطبيق <xliff:g id="APP_NAME_2">%1$s</xliff:g> <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> إشعار.</item>
+      <item quantity="one">يتضمن تطبيق <xliff:g id="APP_NAME_0">%1$s</xliff:g> إشعارًا واحدًا (<xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g>).</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"‏الصفحة %1$d من %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"‏الشاشة الرئيسية %1$d من %2$d"</string>
@@ -83,27 +84,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"المجلد: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"الأدوات"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"الخلفيات"</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>
-    <string name="icon_badging_title" msgid="874121399231955394">"نقاط الإشعارات"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"قيد التشغيل"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"غير مفعّل"</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="icon_badging_service_title" msgid="2309733118428242174">"عرض نقاط الإشعارات"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"عرض نقاط الإشعارات"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"إضافة رمز إلى الشاشة الرئيسية"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"للتطبيقات الجديدة"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"تغيير شكل الرمز"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"على الشاشة الرئيسية"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"استخدام الإعداد التلقائي للنظام"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"مربّع"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"رمز دائري مربّع"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"دائرة"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"رمز على شكل دمعة"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"جارٍ تطبيق تغييرات شكل الرمز"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"غير معروفة"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"إزالة"</string>
     <string name="abandoned_search" msgid="891119232568284442">"بحث"</string>
@@ -114,10 +108,11 @@
     <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="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>
@@ -148,4 +143,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"الإشعارات والتطبيقات متوقفة."</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"إغلاق"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"تمّ الإغلاق"</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 5c896e4..2984603 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -24,8 +24,8 @@
     <string name="work_folder_name" msgid="3753320833950115786">"কৰ্মস্থান"</string>
     <string name="activity_not_found" msgid="8071924732094499514">"এপটো ইনষ্টল কৰা নহ\'ল।"</string>
     <string name="activity_not_available" msgid="7456344436509528827">"এপটো নাই"</string>
-    <string name="safemode_shortcut_error" msgid="9160126848219158407">"ডাউনল\'ড কৰা এপটোক সুৰক্ষিত ম\'ডত অক্ষম কৰা হ\'ল"</string>
-    <string name="safemode_widget_error" msgid="4863470563535682004">"ৱিজেটবোৰক সুৰক্ষিত ম\'ডত অক্ষম কৰা হ\'ল"</string>
+    <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>
@@ -39,6 +39,7 @@
     <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>
@@ -63,76 +64,68 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"ছেটআপ কৰক"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"এইটো এটা ছিষ্টেম এপ আৰু ইয়াক আনইনষ্টল কৰিব নোৱৰি"</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"নামবিহীন ফ\'ল্ডাৰ"</string>
-    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> অক্ষম কৰা হ\'ল"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> অক্ষম কৰা হ’ল"</string>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>ৰ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>টা জাননী আছে</item>
       <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>ৰ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>টা জাননী আছে</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"%2$dৰ %1$d পৃষ্ঠা"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"গৃহ স্ক্ৰীণ %2$dৰ %1$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"গৃহ স্ক্ৰীণৰ নতুন পৃষ্ঠা"</string>
-    <string name="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_opened" msgid="94695026776264709">"ফ’ল্ডাৰ খোলা হ’ল, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"ফ\'ল্ডাৰ বন্ধ কৰিবলৈ টিপক"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"সলনি কৰা নাম ছেভ কৰিবলৈ টিপক"</string>
-    <string name="folder_closed" msgid="4100806530910930934">"ফ\'ল্ডাৰ বন্ধ কৰা হ\'ল"</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" msgid="6629239338071103179">"ফ’ল্ডাৰ: <xliff:g id="NAME">%1$s</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_desc" msgid="8662546029078692509">"ফ\'নটো যেতিয়া ঘূৰোৱা হয়"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"জাননী সম্পৰ্কীয় বিন্দুবোৰ"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"অন অৱস্থাত আছে"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"অফ অৱস্থাত আছে"</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="icon_badging_service_title" msgid="2309733118428242174">"জাননী বিন্দুসমূহ দেখুৱাওক"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"জাননী বিন্দু দেখুৱাওক"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"গৃহ স্ক্ৰীণত আইকনটো যোগ কৰক"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"নতুন এপসমূহৰ বাবে"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"আইকনৰ আকৃতি সলনি কৰক"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"গৃহ স্ক্ৰীণত"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"ছিষ্টেম ডিফ\'ল্ট ব্য়ৱহাৰ কৰক"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"বৰ্গাকাৰ"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"বৰ্গবৃত্তাকাৰ"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"পৰিচিত মানুহৰ গোট"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"চকুপানীৰ টোপাল"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"আইকনৰ আকৃতিত কৰা পৰিবৰ্তনবোৰ প্ৰয়োগ কৰি থকা হৈছে"</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_downloading_title" msgid="8336702962104482644">"<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>
-    <!-- no translation found for widgets_list (796804551140113767) -->
-    <skip />
-    <!-- no translation found for widgets_list_closed (6141506579418771922) -->
-    <skip />
+    <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_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="item_moved" msgid="4606538322571412879">"বস্তুটো স্থানান্তৰ কৰা হ\'ল"</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="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="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="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="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>
     <string name="action_dismiss_notification" msgid="5909461085055959187">"অগ্ৰাহ্য কৰক"</string>
@@ -146,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"জাননী আৰু এপসমূহ অফ হৈ আছে"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"বন্ধ কৰক"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"বন্ধ"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"বিফল: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-az-rAZ/strings.xml b/res/values-az-rAZ/strings.xml
deleted file mode 100644
index ed86f91..0000000
--- a/res/values-az-rAZ/strings.xml
+++ /dev/null
@@ -1,146 +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">
-    <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
-    <string name="folder_name" msgid="7371454440695724752"></string>
-    <string name="work_folder_name" msgid="3753320833950115786">"İş"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"Tətbiq quraşdırılmayıb."</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"Tətbiq əlçatmazdır"</string>
-    <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="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>
-    <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="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="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 məlumatı"</string>
-    <string name="install_drop_target_label" msgid="2539096853673231757">"Quraşdırın"</string>
-    <string name="permlab_install_shortcut" msgid="5632423390354674437">"qısayolları quraşdır"</string>
-    <string name="permdesc_install_shortcut" msgid="923466509822011139">"Tətbiqə istifadəçi müdaxiləsi olmadan qısayolları əlavə etməyə icazə verir."</string>
-    <string name="permlab_read_settings" msgid="1941457408239617576">"Əsas Səhifə ayarlarını və qısayolları oxuyun"</string>
-    <string name="permdesc_read_settings" msgid="5833423719057558387">"Tətbiqə Əsas Səhifədə parametrləri və qısayolları oxumağa icazə verir."</string>
-    <string name="permlab_write_settings" msgid="3574213698004620587">"Əsas Səhifə ayarlarını və qısayolları yazın"</string>
-    <string name="permdesc_write_settings" msgid="5440712911516509985">"Tətbiqə Əsas Səhifədə ayarları və qısayolları dəyişməyə icazə verir."</string>
-    <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> tətbiqinə telefon zəngləri etmək üçün icazə verilmir"</string>
-    <string name="gadget_error_text" msgid="6081085226050792095">"Vidcet yükləmə problemi"</string>
-    <string name="gadget_setup_text" msgid="8274003207686040488">"Quraşdırma"</string>
-    <string name="uninstall_system_app_text" msgid="4172046090762920660">"Bu sistem tətbiqi olduğu üçün sistemdən silinə bilməz."</string>
-    <string name="folder_hint_text" msgid="6617836969016293992">"Adsız Qovluq"</string>
-    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> deaktiv edildi"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> tətbiqində <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> bildiriş var</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> tətbiqində <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> bildiriş var</item>
-    </plurals>
-    <string name="default_scroll_format" msgid="7475544710230993317">"Səhifə %1$d of %2$d"</string>
-    <string name="workspace_scroll_format" msgid="8458889198184077399">"Əsas Səhifə ekranı %1$d of %2$d"</string>
-    <string name="workspace_new_page" msgid="257366611030256142">"Yeni əsas ekran səhifəsi"</string>
-    <string name="folder_opened" msgid="94695026776264709">"Qovluq açıldı, <xliff:g id="HEIGHT">%2$d</xliff:g> hündürlük ilə <xliff:g id="WIDTH">%1$d</xliff:g> enində"</string>
-    <string name="folder_tap_to_close" msgid="4625795376335528256">"Qovluq bağlamaq üçün toxunun"</string>
-    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Ad dəyişikliyini yadda saxlamaq üçün toxunun"</string>
-    <string name="folder_closed" msgid="4100806530910930934">"Qovluq bağlıdır"</string>
-    <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" msgid="6629239338071103179">"Qovluq: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Vidcet"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"Divar kağızları"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Home ayarları"</string>
-    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Admininiz tərəfindən deaktiv edilib"</string>
-    <string name="allow_rotation_title" msgid="7728578836261442095">"Əsas ekranın firlanmağına icazə verin"</string>
-    <string name="allow_rotation_desc" msgid="8662546029078692509">"Telefon çevrilən zaman"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Bildiriş nişanı"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Aktiv"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Deaktiv"</string>
-    <string name="title_missing_notification_access" msgid="7503287056163941064">"Bildiriş girişi tələb edilir"</string>
-    <string name="msg_missing_notification_access" msgid="281113995110910548">"Bildiriş Nöqtələrini göstərmək üçün <xliff:g id="NAME">%1$s</xliff:g> bildirişlərini aktiv edin"</string>
-    <string name="title_change_settings" msgid="1376365968844349552">"Ayarları dəyişin"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Bildiriş nöqtələrini göstərin"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Əsas ekrana ikona əlavə edin"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Yeni tətbiqlər üçün"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"İkona formasını dəyişin"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"Əsas səhifə ekranında"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Sistem defoltu istifadə edin"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Kənarları dairəvi kvadrat"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Çevrə"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Gözyaşı damlası"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"İkona formasına etdiyiniz dəyişikliklər tətbiq edilir"</string>
-    <string name="package_state_unknown" msgid="7592128424511031410">"Naməlum"</string>
-    <string name="abandoned_clean_this" msgid="7610119707847920412">"Yığışdır"</string>
-    <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_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="action_add_to_workspace" msgid="8902165848117513641">"Əsas ekrana əlavə edin"</string>
-    <string name="action_move_here" msgid="2170188780612570250">"Elementi bura köçürün"</string>
-    <string name="item_added_to_workspace" msgid="4211073925752213539">"Element əsas ekrana əlavə edildi"</string>
-    <string name="item_removed" msgid="851119963877842327">"Element silindi"</string>
-    <string name="action_move" msgid="4339390619886385032">"Elementi köçürün"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Sıra <xliff:g id="NUMBER_0">%1$s</xliff:g> sütun <xliff:g id="NUMBER_1">%2$s</xliff:g> köçürün"</string>
-    <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> mövqeyinə köçürün"</string>
-    <string name="move_to_hotseat_position" msgid="6295412897075147808">"<xliff:g id="NUMBER">%1$s</xliff:g> sevimlilər mövqeyinə köçürün"</string>
-    <string name="item_moved" msgid="4606538322571412879">"Elementin yeri dəyişildi"</string>
-    <string name="add_to_folder" msgid="9040534766770853243">"Qovluğa əlavə edin: <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> adlı qovluğa əlavə edin"</string>
-    <string name="added_to_folder" msgid="4793259502305558003">"Element qovluğa əlavə edildi"</string>
-    <string name="create_folder_with" msgid="4050141361160214248">"Qovluq yaradın: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="folder_created" msgid="6409794597405184510">"Qovluq yaradıldı"</string>
-    <string name="action_move_to_workspace" msgid="1603837886334246317">"Əsas ekrana köçürün"</string>
-    <string name="action_resize" msgid="1802976324781771067">"Ölçüsünü dəyişin"</string>
-    <string name="action_increase_width" msgid="8773715375078513326">"Eni artırın"</string>
-    <string name="action_increase_height" msgid="459390020612501122">"Hündürlüyü artırın"</string>
-    <string name="action_decrease_width" msgid="1374549771083094654">"Eni azaldın"</string>
-    <string name="action_decrease_height" msgid="282377193880900022">"Hündürlüyü azaldın"</string>
-    <string name="widget_resized" msgid="9130327887929620">"Vidcetin eni <xliff:g id="NUMBER_0">%1$s</xliff:g> hündürlüyü <xliff:g id="NUMBER_1">%2$s</xliff:g> kimi ölçüləndirildi"</string>
-    <string name="action_deep_shortcut" msgid="2864038805849372848">"Qısa yollar"</string>
-    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> üçün <xliff:g id="APP_NAME">%2$s</xliff:g> qısa yolu"</string>
-    <string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> üçün <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> qısayol və  <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> bildiriş"</string>
-    <string name="action_dismiss_notification" msgid="5909461085055959187">"Rədd edin"</string>
-    <string name="notification_dismissed" msgid="6002233469409822874">"Bildiriş rədd edildi"</string>
-    <string name="all_apps_personal_tab" msgid="4190252696685155002">"Şəxsi"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"İş"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"İş profili"</string>
-    <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Burada iş tətbiqləri axtarın"</string>
-    <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Hər bir iş tətbiqində təşkilat tərəfindən qorunduğunu göstərən narıncı nişan var. Tətbiqləri daha asan giriş üçün Əsas Səhifə Ekranına köçürün."</string>
-    <string name="work_mode_on_label" msgid="4781128097185272916">"Təşkilatınız tərəfindən idarə olunur"</string>
-    <string name="work_mode_off_label" msgid="3194894777601421047">"Bildiriş və tətbiqlər deaktivdir"</string>
-    <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Bağlayın"</string>
-    <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Bağlıdır"</string>
-</resources>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 3b36f37..5528a19 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,7 +65,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Bu sistem tətbiqi olduğu üçün sistemdən silinə bilməz."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Adsız Qovluq"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> deaktiv edildi"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> tətbiqində <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> bildiriş var</item>
       <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> tətbiqində <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> bildiriş var</item>
     </plurals>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Qovluq: <xliff:g id="NAME">%1$s</xliff:g>"</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>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Admininiz tərəfindən deaktiv edilib"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Əsas ekranın firlanmağına icazə verin"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Telefon çevrilən zaman"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Bildiriş nişanı"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Aktiv"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Deaktiv"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Bildiriş nöqtələri"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Aktiv"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Deaktiv"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Bildiriş girişi tələb edilir"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Bildiriş Nöqtələrini göstərmək üçün <xliff:g id="NAME">%1$s</xliff:g> bildirişlərini aktiv edin"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Ayarları dəyişin"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Bildiriş nöqtələrini göstərin"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Bildiriş nöqtələrini göstərin"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Əsas ekrana ikona əlavə edin"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Yeni tətbiqlər üçün"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"İkona formasını dəyişin"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"Əsas səhifə ekranında"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Sistem defoltu istifadə edin"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Kənarları dairəvi kvadrat"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Çevrə"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Gözyaşı damlası"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"İkona formasına etdiyiniz dəyişikliklər tətbiq edilir"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Naməlum"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Yığışdır"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Axtarış"</string>
@@ -114,6 +108,7 @@
     <string name="action_move_here" msgid="2170188780612570250">"Elementi bura köçürün"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Element əsas ekrana əlavə edildi"</string>
     <string name="item_removed" msgid="851119963877842327">"Element silindi"</string>
+    <string name="undo" msgid="4151576204245173321">"Ləğv edin"</string>
     <string name="action_move" msgid="4339390619886385032">"Elementi köçürün"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Sıra <xliff:g id="NUMBER_0">%1$s</xliff:g> sütun <xliff:g id="NUMBER_1">%2$s</xliff:g> köçürün"</string>
     <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> mövqeyinə köçürün"</string>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Bildiriş və tətbiqlər deaktivdir"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Bağlayın"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Bağlıdır"</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 3fb8bf5..883003c 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,10 +65,10 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ovo je sistemska aplikacija i ne može da se deinstalira."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Neimenovani direktorijum"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je onemogućena"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obaveštenje</item>
-      <item quantity="few"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obaveštenja</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obaveštenja</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obaveštenje</item>
+      <item quantity="few"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obaveštenja</item>
+      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obaveštenja</item>
     </plurals>
     <string name="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>
@@ -80,27 +81,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Direktorijum: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Vidžeti"</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>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administrator je onemogućio"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Dozvoli rotaciju početnog ekrana"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Kada se telefon rotira"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Tačke za obaveštenja"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Uključeno"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Isključeno"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Tačke za obaveštenja"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Uključeno"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Isključeno"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Potreban je pristup za obaveštenja"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Da biste prikazali tačke za obaveštenja, uključite obaveštenja za aplikaciju <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Promenite podešavanja"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Prikazuj tačke za obaveštenja"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Prikazuj tačke za obaveštenja"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Dodaj ikonu na početni ekran"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Za nove aplikacije"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Promenite oblik ikona"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"na početnom ekranu"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Koristi podrazumevano sistemsko podešavanje"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Zaobljeni kvadrat"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Krug"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Suza"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Primenjuju se promene oblika ikona"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Nepoznato"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Ukloni"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Pretraži"</string>
@@ -111,10 +105,11 @@
     <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">"Dodaj na početni ekran"</string>
+    <string name="action_add_to_workspace" msgid="8902165848117513641">"Dodajte na početni ekran"</string>
     <string name="action_move_here" msgid="2170188780612570250">"Premesti stavku ovde"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Stavka je dodata na početni ekran"</string>
     <string name="item_removed" msgid="851119963877842327">"Stavka je uklonjena"</string>
+    <string name="undo" msgid="4151576204245173321">"Opozovi"</string>
     <string name="action_move" msgid="4339390619886385032">"Premesti stavku"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Premesti u red <xliff:g id="NUMBER_0">%1$s</xliff:g> i kolonu <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Premesti na <xliff:g id="NUMBER">%1$s</xliff:g>. poziciju"</string>
@@ -145,4 +140,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Obaveštenja i aplikacije su isključeni"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Zatvori"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Zatvoreno"</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-rBY/strings.xml b/res/values-be-rBY/strings.xml
deleted file mode 100644
index 399ae76..0000000
--- a/res/values-be-rBY/strings.xml
+++ /dev/null
@@ -1,148 +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">
-    <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
-    <string name="folder_name" msgid="7371454440695724752"></string>
-    <string name="work_folder_name" msgid="3753320833950115786">"Працоўная"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"Праграма не ўсталявана."</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"Праграма недаступная"</string>
-    <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="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="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="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="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="permlab_install_shortcut" msgid="5632423390354674437">"усталёўваць ярлыкі"</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="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>
-    <string name="gadget_setup_text" msgid="8274003207686040488">"Наладжванне"</string>
-    <string name="uninstall_system_app_text" msgid="4172046090762920660">"Гэта сістэмная праграма, яе нельга выдаліць."</string>
-    <string name="folder_hint_text" msgid="6617836969016293992">"Папка без назвы"</string>
-    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> адключана"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>: ёсць <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> апавяшчэнне</item>
-      <item quantity="few"><xliff:g id="APP_NAME_2">%1$s</xliff:g>: ёсць <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> апавяшчэнні</item>
-      <item quantity="many"><xliff:g id="APP_NAME_2">%1$s</xliff:g>: ёсць <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> апавяшчэнняў</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>: ёсць <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> апавяшчэння</item>
-    </plurals>
-    <string name="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_name_format" msgid="6629239338071103179">"Папка: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Віджэты"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"Шпалеры"</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="icon_badging_title" msgid="874121399231955394">"Значкі апавяшчэнняў"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Уключана"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Выключана"</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="icon_badging_service_title" msgid="2309733118428242174">"Паказаць значкі апавяшчэнняў"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Дадаць значок на Галоўны экран"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Для новых праграм"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Змяніць форму значка"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"на галоўным экране"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Выкарыстоўваць стандартныя формы"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Квадрат"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Прамавугольнік са скругленымі вугламі"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Круг"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Сляза"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Змены формы значка прымяняюцца"</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_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="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="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="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>
-    <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_description" msgid="406159963824238648">"Ярлыкі (<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>) для <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
-    <string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"Ярлыкі (<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>) і апавяшчэнні (<xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g>) для <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
-    <string name="action_dismiss_notification" msgid="5909461085055959187">"Адхіліць"</string>
-    <string name="notification_dismissed" msgid="6002233469409822874">"Апавяшчэнне адхілена"</string>
-    <string name="all_apps_personal_tab" msgid="4190252696685155002">"Асабістыя"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"Праца"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"Працоўны профіль"</string>
-    <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Знайдзіце працоўныя праграмы тут"</string>
-    <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Кожная працоўная праграма мае значок і знаходзіцца пад аховай вашай арганізацыі. Для больш простага доступу перамясціце праграмы на Галоўны экран."</string>
-    <string name="work_mode_on_label" msgid="4781128097185272916">"Пад кіраваннем вашай арганізацыі"</string>
-    <string name="work_mode_off_label" msgid="3194894777601421047">"Апавяшчэнні і праграмы выключаны"</string>
-    <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Закрыць"</string>
-    <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Закрытыя"</string>
-</resources>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 59a8f1f..b4cf913 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -49,7 +50,7 @@
     <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="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="permlab_install_shortcut" msgid="5632423390354674437">"усталёўваць ярлыкі"</string>
@@ -64,11 +65,11 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Гэта сістэмная праграма, яе нельга выдаліць."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Папка без назвы"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> адключана"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>: ёсць <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> апавяшчэнне</item>
-      <item quantity="few"><xliff:g id="APP_NAME_2">%1$s</xliff:g>: ёсць <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> апавяшчэнні</item>
-      <item quantity="many"><xliff:g id="APP_NAME_2">%1$s</xliff:g>: ёсць <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> апавяшчэнняў</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>: ёсць <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> апавяшчэння</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, мае <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> апавяшчэнне</item>
+      <item quantity="few"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, мае <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> апавяшчэнні</item>
+      <item quantity="many"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, мае <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> апавяшчэнняў</item>
+      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, мае <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> апавяшчэння</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"Старонка %1$d з %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Галоўны экран %1$d з %2$d"</string>
@@ -81,27 +82,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Папка: <xliff:g id="NAME">%1$s</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="icon_badging_title" msgid="874121399231955394">"Значкі апавяшчэнняў"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Уключана"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Выключана"</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="icon_badging_service_title" msgid="2309733118428242174">"Паказаць значкі апавяшчэнняў"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Паказваць значкі апавяшчэнняў"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Дадаць значок на Галоўны экран"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Для новых праграм"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Змяніць форму значка"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"на галоўным экране"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Выкарыстоўваць стандартныя формы"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Квадрат"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Прамавугольнік са скругленымі вугламі"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Круг"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Сляза"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Змены формы значка прымяняюцца"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Невядома"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Выдаліць"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Шукаць"</string>
@@ -116,6 +110,7 @@
     <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>
@@ -146,4 +141,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Апавяшчэнні і праграмы выключаны"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Закрыць"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Закрытыя"</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 47825f6..408f205 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,7 +65,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Това е системно приложение и не може да се деинсталира."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Папка без име"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Деактивирахте <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> – има <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> известия</item>
       <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> – има <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> известие</item>
     </plurals>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Папка: „<xliff:g id="NAME">%1$s</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="icon_badging_title" msgid="874121399231955394">"Точки за известия"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Включено"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Изключено"</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="icon_badging_service_title" msgid="2309733118428242174">"Показване на точките за известия"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Показване на точките за известия"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Добавяне на икона към началния екран"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"За нови приложения"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Промяна на формата на иконите"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"на началния екран"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Използване на стандартната системна настройка"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Квадрат"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Комбинация от кръг и квадрат"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Кръг"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Сълза"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Промените във формата на иконите се прилагат"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Няма информация"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Премахване"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Търсене"</string>
@@ -114,6 +108,7 @@
     <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>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Известията и приложенията са изключени"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Затваряне"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Затворено"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"Неуспешно: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-bn-rBD/strings.xml b/res/values-bn-rBD/strings.xml
deleted file mode 100644
index ffeeed7..0000000
--- a/res/values-bn-rBD/strings.xml
+++ /dev/null
@@ -1,146 +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">
-    <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
-    <string name="folder_name" msgid="7371454440695724752"></string>
-    <string name="work_folder_name" msgid="3753320833950115786">"কাজ"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"অ্যাপ্লিকেশান ইনস্টল করা নেই৷"</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"অ্যাপ্লিকেশান অনুপলব্ধ"</string>
-    <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="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>
-    <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="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="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="permlab_install_shortcut" msgid="5632423390354674437">"শর্টকাটগুলি ইনস্টল করে"</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="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>
-    <string name="gadget_setup_text" msgid="8274003207686040488">"সেটআপ"</string>
-    <string name="uninstall_system_app_text" msgid="4172046090762920660">"এটি একটি সিস্টেম অ্যাপ্লিকেশান এবং আনইনস্টল করা যাবে না৷"</string>
-    <string name="folder_hint_text" msgid="6617836969016293992">"নামবিহীন ফোল্ডার"</string>
-    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> অক্ষম করা হয়েছে"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> এ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>টি বিজ্ঞপ্তি আছে</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> এ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>টি বিজ্ঞপ্তি আছে</item>
-    </plurals>
-    <string name="default_scroll_format" msgid="7475544710230993317">"%2$dটির মধ্যে %1$dটি পৃষ্ঠা"</string>
-    <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$dটির %1$d নম্বর হোম স্ক্রিন"</string>
-    <string name="workspace_new_page" msgid="257366611030256142">"নতুন হোম স্ক্রীনের পৃষ্ঠা"</string>
-    <string name="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" msgid="6629239338071103179">"ফোল্ডার: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"উইজেট"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"ওয়ালপেপারগুলি"</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="icon_badging_title" msgid="874121399231955394">"বিজ্ঞপ্তি ডট"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"চালু হয়েছে"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"বন্ধ আছে"</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="icon_badging_service_title" msgid="2309733118428242174">"বিজ্ঞপ্তির ডট দেখুন"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"হোম স্ক্রিনে আইকন যোগ করুন"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"নতুন অ্যাপ্লিকেশানগুলির জন্যে"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"আইকনের আকৃতি পরিবর্তন করুন"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"হোম স্ক্রিনে"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"সিস্টেমের ডিফল্ট মান ব্যবহার করুন"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"চৌকো"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"চৌকো-গোলাকার"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"গোলাকার"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"চোখের জল"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"আইকনের আকৃতি পরিবর্তন করা হচ্ছে"</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_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="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="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="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>
-    <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_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> এর <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>টি শর্টকার্ট"</string>
-    <string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> এর জন্য <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>টি শর্টকাট এবং <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g>টি বিজ্ঞপ্তি"</string>
-    <string name="action_dismiss_notification" msgid="5909461085055959187">"খারিজ করুন"</string>
-    <string name="notification_dismissed" msgid="6002233469409822874">"বিজ্ঞপ্তি খারিজ করা হয়েছে"</string>
-    <string name="all_apps_personal_tab" msgid="4190252696685155002">"ব্যক্তিগত"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"অফিস"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"অফিসের প্রোফাইল"</string>
-    <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"এখানে কাজের অ্যাপ্সগুলি খুঁজুন"</string>
-    <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"প্রতিটি কাজের অ্যাপে একটি করে ব্যাজ রয়েছে এবং অ্যাপগুলি আপনার প্রতিষ্ঠানের দ্বারা সুরক্ষিত। সহজে অ্যাক্সেস করার জন্য অ্যাপগুলি হোম স্ক্রিনে রাখুন।"</string>
-    <string name="work_mode_on_label" msgid="4781128097185272916">"আপনার প্রতিষ্ঠানের দ্বারা পরিচালিত"</string>
-    <string name="work_mode_off_label" msgid="3194894777601421047">"বিজ্ঞপ্তি এবং অ্যাপ বন্ধ আছে"</string>
-    <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"বন্ধ করুন"</string>
-    <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"বন্ধ"</string>
-</resources>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index 651f400..775885c 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,9 +65,9 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"এটি একটি সিস্টেম অ্যাপ্লিকেশান এবং আনইনস্টল করা যাবে না৷"</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"নামবিহীন ফোল্ডার"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> অক্ষম করা হয়েছে"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> এ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>টি বিজ্ঞপ্তি আছে</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> এ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>টি বিজ্ঞপ্তি আছে</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>টি বিজ্ঞপ্তি আছে</item>
+      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>টি বিজ্ঞপ্তি আছে</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"%2$dটির মধ্যে %1$dটি পৃষ্ঠা"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$dটির %1$d নম্বর হোম স্ক্রিন"</string>
@@ -79,43 +80,35 @@
     <string name="folder_name_format" msgid="6629239338071103179">"ফোল্ডার: <xliff:g id="NAME">%1$s</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="icon_badging_title" msgid="874121399231955394">"বিজ্ঞপ্তি ডট"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"চালু হয়েছে"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"বন্ধ আছে"</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="icon_badging_service_title" msgid="2309733118428242174">"বিজ্ঞপ্তির ডট দেখুন"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"বিজ্ঞপ্তির ডট দেখুন"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"হোম স্ক্রিনে আইকন যোগ করুন"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"নতুন অ্যাপ্লিকেশানগুলির জন্যে"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"আইকনের আকৃতি পরিবর্তন করুন"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"হোম স্ক্রিনে"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"সিস্টেমের ডিফল্ট মান ব্যবহার করুন"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"চৌকো"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"চৌকো-গোলাকার"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"গোলাকার"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"চোখের জল"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"আইকনের আকৃতি পরিবর্তন করা হচ্ছে"</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_search" msgid="891119232568284442">"সার্চ"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"এই অ্যাপ্লিকেশানটি ইন্সটল করা নাই"</string>
-    <string name="abandoned_promise_explanation" msgid="3990027586878167529">"এই আইকনের অ্যাপ্লিকেশানটি ইন্সটল করা নাই। আপনি এটি সরাতে পারেন বা অ্যাপ্লিকেশানটি অনুসন্ধান করে এটি নিজে ইন্সটল করতে পারেন।"</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_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>
-    <!-- no translation found for widgets_list (796804551140113767) -->
-    <skip />
-    <!-- no translation found for widgets_list_closed (6141506579418771922) -->
-    <skip />
-    <string name="action_add_to_workspace" msgid="8902165848117513641">"হোম স্ক্রীনে যোগ করুন"</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="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>
@@ -146,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"বিজ্ঞপ্তি এবং অ্যাপ বন্ধ আছে"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"বন্ধ করুন"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"বন্ধ"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"কাজটি করা যায়নি: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-bs-rBA/strings.xml b/res/values-bs-rBA/strings.xml
deleted file mode 100644
index 40c8866..0000000
--- a/res/values-bs-rBA/strings.xml
+++ /dev/null
@@ -1,147 +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">
-    <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
-    <string name="folder_name" msgid="7371454440695724752"></string>
-    <string name="work_folder_name" msgid="3753320833950115786">"Posao"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"Aplikacija nije instalirana."</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"Aplikacija nije dostupna"</string>
-    <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="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>
-    <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="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="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">"Spisak 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">"Informacije o aplikaciji"</string>
-    <string name="install_drop_target_label" msgid="2539096853673231757">"Instaliraj"</string>
-    <string name="permlab_install_shortcut" msgid="5632423390354674437">"instaliraj prečice"</string>
-    <string name="permdesc_install_shortcut" msgid="923466509822011139">"Dopušta aplikaciji dodavanje prečica bez posredovanja korisnika."</string>
-    <string name="permlab_read_settings" msgid="1941457408239617576">"čitaj postavke na početnom ekranu i prečice"</string>
-    <string name="permdesc_read_settings" msgid="5833423719057558387">"Dopušta aplikaciji čitanje postavki i prečica na početnom ekranu."</string>
-    <string name="permlab_write_settings" msgid="3574213698004620587">"zapisuj postavke na početnom ekranu i prečice"</string>
-    <string name="permdesc_write_settings" msgid="5440712911516509985">"Dopušta aplikaciji promjenu postavki i prečica na početnom ekranu."</string>
-    <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> nema odobrenje da uspostavlja telefonske pozive"</string>
-    <string name="gadget_error_text" msgid="6081085226050792095">"Problem pri učitavanju dodatka"</string>
-    <string name="gadget_setup_text" msgid="8274003207686040488">"Postavljanje"</string>
-    <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ovo je sistemska aplikacija i ne može se deinstalirati."</string>
-    <string name="folder_hint_text" msgid="6617836969016293992">"Neimenovani folder"</string>
-    <string name="disabled_app_label" msgid="6673129024321402780">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je onemogućena"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obavještenje</item>
-      <item quantity="few"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obavještenja</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obavještenja</item>
-    </plurals>
-    <string name="default_scroll_format" msgid="7475544710230993317">"Strana %1$d od %2$d"</string>
-    <string name="workspace_scroll_format" msgid="8458889198184077399">"Početni ekran %1$d od %2$d"</string>
-    <string name="workspace_new_page" msgid="257366611030256142">"Nova stranica početnog ekrana"</string>
-    <string name="folder_opened" msgid="94695026776264709">"Folder je otvoren, (š) <xliff:g id="WIDTH">%1$d</xliff:g> (v) <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="4625795376335528256">"Dodirnite da zatvorite folder"</string>
-    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Dodirnite da sačuvate promjenu naziva"</string>
-    <string name="folder_closed" msgid="4100806530910930934">"Folder je zatvoren"</string>
-    <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" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Dodaci"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"Pozadinske slike"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Postavke početnog ekrana"</string>
-    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Onemogućio vaš administrator"</string>
-    <string name="allow_rotation_title" msgid="7728578836261442095">"Dozvoli rotiranje početnog ekrana"</string>
-    <string name="allow_rotation_desc" msgid="8662546029078692509">"Kada se telefon zarotira"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Tačke za obavještenja"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Uključeno"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Isključeno"</string>
-    <string name="title_missing_notification_access" msgid="7503287056163941064">"Potreban je pristup obavještenjima"</string>
-    <string name="msg_missing_notification_access" msgid="281113995110910548">"Za prikaz tačaka obavještenja, uključite obavještenja za aplikacije za aplikaciju <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="title_change_settings" msgid="1376365968844349552">"Promijeni postavke"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Prikaži tačke za obavještenja"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Dodaj ikonu na početni ekran"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Za nove aplikacije"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Promjena oblika ikona"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"na Početnom ekranu"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Koristite sistemski zadano"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Zaobljeni kvadrat"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Krug"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Suza"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Primjenjivanje promjena oblika ikona"</string>
-    <string name="package_state_unknown" msgid="7592128424511031410">"Nepoznato"</string>
-    <string name="abandoned_clean_this" msgid="7610119707847920412">"Ukloni"</string>
-    <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_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="action_add_to_workspace" msgid="8902165848117513641">"Dodaj 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>
-    <string name="action_move" msgid="4339390619886385032">"Premjesti stavku"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Pomjeri stavku u red <xliff:g id="NUMBER_0">%1$s</xliff:g> kolonu <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
-    <string name="move_to_position" msgid="6750008980455459790">"Pomjeri stavku na poziciju <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
-    <string name="move_to_hotseat_position" msgid="6295412897075147808">"Pomjeri stavku na poziciju <xliff:g id="NUMBER">%1$s</xliff:g> među omiljenim"</string>
-    <string name="item_moved" msgid="4606538322571412879">"Stavka je premještena"</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 sa aplikacijom <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="added_to_folder" msgid="4793259502305558003">"Stavka je dodana u folder"</string>
-    <string name="create_folder_with" msgid="4050141361160214248">"Kreirajte folder sa stavkom: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="folder_created" msgid="6409794597405184510">"Folder je kreiran"</string>
-    <string name="action_move_to_workspace" msgid="1603837886334246317">"Pomjeri na početni ekran"</string>
-    <string name="action_resize" msgid="1802976324781771067">"Promijeni veličinu"</string>
-    <string name="action_increase_width" msgid="8773715375078513326">"Povećaj širinu"</string>
-    <string name="action_increase_height" msgid="459390020612501122">"Povećaj visinu"</string>
-    <string name="action_decrease_width" msgid="1374549771083094654">"Smanji širinu"</string>
-    <string name="action_decrease_height" msgid="282377193880900022">"Smanji visinu"</string>
-    <string name="widget_resized" msgid="9130327887929620">"Veličina vidžeta je promijenjena na širinu <xliff:g id="NUMBER_0">%1$s</xliff:g> visinu <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
-    <string name="action_deep_shortcut" msgid="2864038805849372848">"Prečice"</string>
-    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> prečica za aplikaciju <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
-    <string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"Za aplikaciju <xliff:g id="APP_NAME">%3$s</xliff:g> broj prečica je <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>, a broj obavještenja je <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g>"</string>
-    <string name="action_dismiss_notification" msgid="5909461085055959187">"Odbaci"</string>
-    <string name="notification_dismissed" msgid="6002233469409822874">"Obavještenje je odbačeno"</string>
-    <string name="all_apps_personal_tab" msgid="4190252696685155002">"Lične"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"Poslovne"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"Radni profil"</string>
-    <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Pronađite poslovne aplikacije ovdje"</string>
-    <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Svaka poslovna aplikacija ima značku i osigurava je vaša organizacija. Premjestite aplikacije na Početni ekran, radi lakšeg pristupa."</string>
-    <string name="work_mode_on_label" msgid="4781128097185272916">"Upravlja vaša organizacija"</string>
-    <string name="work_mode_off_label" msgid="3194894777601421047">"Notifikacije i aplikacije su isključene"</string>
-    <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Zatvori"</string>
-    <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Zatvoreno"</string>
-</resources>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 848176d..5650457 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,10 +65,10 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ovo je sistemska aplikacija i ne može se deinstalirati."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Neimenovani folder"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je onemogućena"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obavještenje</item>
-      <item quantity="few"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obavještenja</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obavještenja</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="one">Aplikacija <xliff:g id="APP_NAME_2">%1$s</xliff:g> ima<xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obavještenje​</item>
+      <item quantity="few">Aplikacija <xliff:g id="APP_NAME_2">%1$s</xliff:g> ima<xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obavještenja​</item>
+      <item quantity="other">Aplikacija <xliff:g id="APP_NAME_2">%1$s</xliff:g> ima<xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obavještenja​</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"Strana %1$d od %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Početni ekran %1$d od %2$d"</string>
@@ -78,29 +79,22 @@
     <string name="folder_closed" msgid="4100806530910930934">"Folder je zatvoren"</string>
     <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" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Dodaci"</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>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Onemogućio vaš administrator"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Dozvoli rotiranje početnog ekrana"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Kada se telefon zarotira"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Tačke za obavještenja"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Uključeno"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Isključeno"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Tačke za obavještenja"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Uključeno"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Isključeno"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Potreban je pristup obavještenjima"</string>
-    <string name="msg_missing_notification_access" msgid="281113995110910548">"Za prikaz tačaka obavještenja, uključite obavještenja za aplikacije za aplikaciju <xliff:g id="NAME">%1$s</xliff:g>"</string>
+    <string name="msg_missing_notification_access" msgid="281113995110910548">"Za prikaz tačaka za obavještenja, uključite obavještenja za aplikacije za aplikaciju <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Promijeni postavke"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Prikaži tačke za obavještenja"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Prikaži tačke za obavještenja"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Dodaj ikonu na početni ekran"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Za nove aplikacije"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Promjena oblika ikona"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"na Početnom ekranu"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Koristite sistemski zadano"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Zaobljeni kvadrat"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Krug"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Suza"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Primjenjivanje promjena oblika ikona"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Nepoznato"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Ukloni"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Pretraži"</string>
@@ -115,6 +109,7 @@
     <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>
+    <string name="undo" msgid="4151576204245173321">"Poništi"</string>
     <string name="action_move" msgid="4339390619886385032">"Premjesti stavku"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Pomjeri stavku u red <xliff:g id="NUMBER_0">%1$s</xliff:g> kolonu <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Pomjeri stavku na poziciju <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
@@ -145,4 +140,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Notifikacije i aplikacije su isključene"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Zatvori"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Zatvoreno"</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 27f9c31..c0859d3 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -33,12 +33,13 @@
     <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="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">"Toca i mantén premut l\'element per col·locar-lo manualment"</string>
+    <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Mantén premut l\'element per afegir-lo manualment"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Afegeix automàticament"</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>
@@ -64,9 +65,9 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Aquesta aplicació és una aplicació del sistema i no es pot desinstal·lar."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Carpeta sense nom"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"S\'ha desactivat <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> té <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificacions</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> té <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notificació</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>​ té <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificacions</item>
+      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> té <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notificació</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"Pàgina %1$d de %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Pantalla d\'inici %1$d de %2$d"</string>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fons de pantalla"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Configuració de pantalla d\'inici"</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>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Desactivada per l\'administrador"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Permet la rotació de la pantalla d\'inici"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"En girar el telèfon"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Punts de notificació"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Activat"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Desactivat"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Punts de notificació"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Activats"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Desactivats"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Cal que tingui accés a les notificacions"</string>
     <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="icon_badging_service_title" msgid="2309733118428242174">"Mostra els punts de notificació"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Afegeix la icona a la pantalla d\'inici"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Mostra els punts de notificació"</string>
+    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Afegeix icona a la pantalla d\'inici"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Per a les aplicacions noves"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Canvia la forma de les icones"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"a la pantalla d\'inici"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Utilitza l\'opció predeterminada del sistema"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Quadrat"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Quadrat arrodonit"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Cercle"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Llàgrima"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"S\'estan aplicant els canvis de forma de les icones"</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>
@@ -114,6 +108,7 @@
     <string name="action_move_here" msgid="2170188780612570250">"Mou l\'element aquí"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"S\'ha afegit l\'element a la pantalla d\'inici"</string>
     <string name="item_removed" msgid="851119963877842327">"S\'ha suprimit l\'element"</string>
+    <string name="undo" msgid="4151576204245173321">"Desfés"</string>
     <string name="action_move" msgid="4339390619886385032">"Desplaça l\'element"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Desplaça l\'element a la fila <xliff:g id="NUMBER_0">%1$s</xliff:g> i la columna <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Desplaça l\'element a la posició <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Les notificacions i les aplicacions estan desactivades"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Tanca"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"S\'ha tancat"</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 48adf44..692b57d 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,7 +65,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Toto je systémová aplikace a nelze ji odinstalovat."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Složka bez názvu"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> je zakázána"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="few">Aplikace <xliff:g id="APP_NAME_2">%1$s</xliff:g> má <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> oznámení</item>
       <item quantity="many">Aplikace <xliff:g id="APP_NAME_2">%1$s</xliff:g> má <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> oznámení</item>
       <item quantity="other">Aplikace <xliff:g id="APP_NAME_2">%1$s</xliff:g> má <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> oznámení</item>
@@ -81,27 +82,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Složka: <xliff:g id="NAME">%1$s</xliff:g>"</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>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Zakázáno administrátorem"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Povolit otáčení plochy"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Při otočení telefonu"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Puntíky s oznámením"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Zapnuto"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Vypnuto"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Puntíky s oznámením"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Zapnuto"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Vypnuto"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Je třeba udělit přístup k oznámením"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Chcete-li zobrazovat puntíky s oznámením, zapněte oznámení z aplikace <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Změnit nastavení"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Zobrazovat puntíky s oznámením"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Zobrazovat puntíky s oznámením"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Přidat ikonu na plochu"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Pro nové aplikace"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Změnit tvar ikony"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"na ploše"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Použít výchozí nastavení systému"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Čtverec"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Kruh/čtverec"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Kruh"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Slza"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Tvar ikony se mění"</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>
@@ -116,6 +110,7 @@
     <string name="action_move_here" msgid="2170188780612570250">"Přesunout položku sem"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Položka byla přidána na plochu"</string>
     <string name="item_removed" msgid="851119963877842327">"Položka byla odstraněna"</string>
+    <string name="undo" msgid="4151576204245173321">"Zpět"</string>
     <string name="action_move" msgid="4339390619886385032">"Přesunout položku"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Přesunout na řádek <xliff:g id="NUMBER_0">%1$s</xliff:g> do sloupce <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Přesunout na pozici <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
@@ -146,4 +141,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Oznámení a aplikace jsou vypnuty"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Zavřít"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Zavřeno"</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 d141ad9..dc17516 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -33,21 +33,22 @@
     <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="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 fingeren nede for at placere manuelt"</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>
     <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="notifications_header" msgid="1404149926117359025">"Underretninger"</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="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 Foretrukne"</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">"Startskærm"</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>
@@ -64,9 +65,9 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Dette er en systemapp, som ikke kan afinstalleres."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Unavngiven mappe"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> er deaktiveret"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> har <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> underretning</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> har <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> underretninger</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, har <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifikation</item>
+      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, har <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifikationer</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"Side %1$d ud af %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Startskærm %1$d ud af %2$d"</string>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Mappe: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Baggrunde"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Indstillinger for startskærmen"</string>
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stil og baggrunde"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Startskærmindstillinger"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Deaktiveret af din administrator"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Tillad rotation af startskærmen"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Når telefonen roteres"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Underretningscirkler"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Til"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Fra"</string>
-    <string name="title_missing_notification_access" msgid="7503287056163941064">"Kræver adgang til underretninger"</string>
-    <string name="msg_missing_notification_access" msgid="281113995110910548">"Hvis du vil se underretningscirkler, skal du aktivere appunderretninger for <xliff:g id="NAME">%1$s</xliff:g>"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Notifikationscirkler"</string>
+    <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="title_change_settings" msgid="1376365968844349552">"Skift indstillinger"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Vis underretningscirkler"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Vis notifikationscirkler"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Føj ikon til startskærmen"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"For nye apps"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Skift ikonform"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"på startskærmen"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Brug systemstandarden"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Kvadrat med runde hjørner"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Cirkel"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Dråbeform"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Anvender ændringer af ikonform"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Ukendt"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Fjern"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Søg"</string>
@@ -114,6 +108,7 @@
     <string name="action_move_here" msgid="2170188780612570250">"Flyt elementet hertil"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Elementet er føjet til startskærmen"</string>
     <string name="item_removed" msgid="851119963877842327">"Elementet er fjernet"</string>
+    <string name="undo" msgid="4151576204245173321">"Fortryd"</string>
     <string name="action_move" msgid="4339390619886385032">"Flyt element"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Flyt til række <xliff:g id="NUMBER_0">%1$s</xliff:g>, kolonne <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Flyt til position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
@@ -132,16 +127,17 @@
     <string name="action_decrease_height" msgid="282377193880900022">"Reducer højden"</string>
     <string name="widget_resized" msgid="9130327887929620">"Størrelsen for widgetten er ændret til bredde <xliff:g id="NUMBER_0">%1$s</xliff:g> og højde <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
     <string name="action_deep_shortcut" msgid="2864038805849372848">"Genveje"</string>
-    <string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Genveje og underretninger"</string>
+    <string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Genveje og notifikationer"</string>
     <string name="action_dismiss_notification" msgid="5909461085055959187">"Afvis"</string>
-    <string name="notification_dismissed" msgid="6002233469409822874">"Underretningen blev afvist"</string>
+    <string name="notification_dismissed" msgid="6002233469409822874">"Notifikationen blev afvist"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personlige"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Arbejde"</string>
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"Arbejdsprofil"</string>
     <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Find arbejdsapps her"</string>
     <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Alle arbejdsapps har et badge og beskyttes af din organisation. Flyt apps til din startskærm, så du nemmere kan få adgang til dem."</string>
     <string name="work_mode_on_label" msgid="4781128097185272916">"Administreret af din organisation"</string>
-    <string name="work_mode_off_label" msgid="3194894777601421047">"Underretninger og apps er slået fra"</string>
+    <string name="work_mode_off_label" msgid="3194894777601421047">"Notifikationer og apps er slået fra"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Luk"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Lukket"</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 c309a55..a345bab 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,7 +65,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Dies ist eine Systemanwendung, die nicht deinstalliert werden kann."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Unbenannter Ordner"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> deaktiviert"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, hat <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> Benachrichtigungen</item>
       <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, hat <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> Benachrichtigung</item>
     </plurals>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Ordner: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Hintergründe"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Einstellungen für den Startbildschirm"</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_desc" msgid="8662546029078692509">"Bei Drehung des Smartphones"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"App-Benachrichtigungspunkte"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Aktiviert"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Deaktiviert"</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>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Aus"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Benachrichtigungszugriff erforderlich"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Um dir Benachrichtigungspunkte anzeigen zu lassen, aktiviere die Benachrichtigungen für die App \"<xliff:g id="NAME">%1$s</xliff:g>\""</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Einstellungen ändern"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"App-Benachrichtigungspunkte anzeigen"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Symbol zu Startbildschirm hinzufügen"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Für neue Apps"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Form des Symbols ändern"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"auf dem Startbildschirm"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Systemstandardeinstellung verwenden"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Quadrat"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Superkreis"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Kreis"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Träne"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Änderungen an der Form des Symbols werden übernommen"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"App-Benachrichtigungspunkte anzeigen"</string>
+    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Symbol zum Startbildschirm hinzufügen"</string>
+    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Bei neuen Apps"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Unbekannt"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Entfernen"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Suchen"</string>
@@ -114,6 +108,7 @@
     <string name="action_move_here" msgid="2170188780612570250">"Element hierhin verschieben"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Element zum Startbildschirm hinzugefügt"</string>
     <string name="item_removed" msgid="851119963877842327">"Element entfernt"</string>
+    <string name="undo" msgid="4151576204245173321">"Rückgängig"</string>
     <string name="action_move" msgid="4339390619886385032">"Element verschieben"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"In Zeile <xliff:g id="NUMBER_0">%1$s</xliff:g>, Spalte <xliff:g id="NUMBER_1">%2$s</xliff:g> verschoben"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Auf Position <xliff:g id="NUMBER">%1$s</xliff:g> verschoben"</string>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Benachrichtigungen und Apps sind deaktiviert"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Schließen"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Geschlossen"</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 3208fcb..2363e61 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,9 +65,9 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Αυτή είναι μια εφαρμογή συστήματος και δεν είναι δυνατή η κατάργηση της εγκατάστασής της."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Φάκελος χωρίς όνομα"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> είναι απενεργοποιημένη"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other">Η εφαρμογή <xliff:g id="APP_NAME_2">%1$s</xliff:g> έχει <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ειδοποιήσεις</item>
-      <item quantity="one">Η εφαρμογή <xliff:g id="APP_NAME_0">%1$s</xliff:g> έχει <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> ειδοποίηση</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="other">Η εφαρμογή <xliff:g id="APP_NAME_2">%1$s</xliff:g>, έχει <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ειδοποιήσεις</item>
+      <item quantity="one">Η εφαρμογή <xliff:g id="APP_NAME_0">%1$s</xliff:g>, έχει <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> ειδοποίηση</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"Σελίδα %1$d από %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Αρχική οθόνη %1$d από %2$d"</string>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Φάκελος: <xliff:g id="NAME">%1$s</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="icon_badging_title" msgid="874121399231955394">"Κουκκίδες ειδοποίησης"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Ενεργή"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Ανενεργή"</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="icon_badging_service_title" msgid="2309733118428242174">"Εμφάνιση κουκκίδων ειδοποίησης"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Εμφάνιση κουκκίδων ειδοποιήσεων"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Προσθήκη εικονιδίου στην Αρχική οθόνη"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Για νέες εφαρμογές"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Αλλαγή σχήματος εικονιδίου"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"στην Αρχική οθόνη"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Χρήση προεπιλογής συστήματος"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Τετράγωνο"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Στρογγυλεμένο τετράγωνο"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Κύκλος"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Δάκρυ"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Εφαρμογή αλλαγών σχήματος εικονιδίων"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Άγνωστο"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Κατάργηση"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Αναζήτηση"</string>
@@ -114,6 +108,7 @@
     <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>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Οι ειδοποιήσεις και οι εφαρμογές είναι απενεργοποιημένες"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Κλείσιμο"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Κλειστή"</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 245ee43..0c48b5d 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,7 +65,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"This is a system app and can\'t be uninstalled."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Unnamed Folder"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Disabled <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, has <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifications</item>
       <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, has <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notification</item>
     </plurals>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</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>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Disabled by your admin"</string>
-    <string name="allow_rotation_title" msgid="7728578836261442095">"Allow Homescreen rotation"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Allow Home screen rotation"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"When phone is rotated"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Notification dots"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"On"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Off"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Notification dots"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"On"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Off"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Notification access needed"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"To show Notification Dots, turn on app notifications for <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Change settings"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Show notification dots"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Show notification dots"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Add icon to Home screen"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"For new apps"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Change icon shape"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"on Home screen"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Use system default"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Square"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Squircle"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Circle"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Teardrop"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Applying icon shape changes"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Unknown"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Remove"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Search"</string>
@@ -114,6 +108,7 @@
     <string name="action_move_here" msgid="2170188780612570250">"Move item here"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Item added to home screen"</string>
     <string name="item_removed" msgid="851119963877842327">"Item removed"</string>
+    <string name="undo" msgid="4151576204245173321">"Undo"</string>
     <string name="action_move" msgid="4339390619886385032">"Move item"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Move to row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Move to position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Notifications and apps are off"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Close"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Closed"</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 245ee43..0c48b5d 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,7 +65,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"This is a system app and can\'t be uninstalled."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Unnamed Folder"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Disabled <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, has <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifications</item>
       <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, has <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notification</item>
     </plurals>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</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>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Disabled by your admin"</string>
-    <string name="allow_rotation_title" msgid="7728578836261442095">"Allow Homescreen rotation"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Allow Home screen rotation"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"When phone is rotated"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Notification dots"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"On"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Off"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Notification dots"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"On"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Off"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Notification access needed"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"To show Notification Dots, turn on app notifications for <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Change settings"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Show notification dots"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Show notification dots"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Add icon to Home screen"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"For new apps"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Change icon shape"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"on Home screen"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Use system default"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Square"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Squircle"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Circle"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Teardrop"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Applying icon shape changes"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Unknown"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Remove"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Search"</string>
@@ -114,6 +108,7 @@
     <string name="action_move_here" msgid="2170188780612570250">"Move item here"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Item added to home screen"</string>
     <string name="item_removed" msgid="851119963877842327">"Item removed"</string>
+    <string name="undo" msgid="4151576204245173321">"Undo"</string>
     <string name="action_move" msgid="4339390619886385032">"Move item"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Move to row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Move to position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Notifications and apps are off"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Close"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Closed"</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 245ee43..0c48b5d 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,7 +65,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"This is a system app and can\'t be uninstalled."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Unnamed Folder"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Disabled <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, has <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifications</item>
       <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, has <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notification</item>
     </plurals>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</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>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Disabled by your admin"</string>
-    <string name="allow_rotation_title" msgid="7728578836261442095">"Allow Homescreen rotation"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Allow Home screen rotation"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"When phone is rotated"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Notification dots"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"On"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Off"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Notification dots"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"On"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Off"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Notification access needed"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"To show Notification Dots, turn on app notifications for <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Change settings"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Show notification dots"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Show notification dots"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Add icon to Home screen"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"For new apps"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Change icon shape"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"on Home screen"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Use system default"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Square"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Squircle"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Circle"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Teardrop"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Applying icon shape changes"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Unknown"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Remove"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Search"</string>
@@ -114,6 +108,7 @@
     <string name="action_move_here" msgid="2170188780612570250">"Move item here"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Item added to home screen"</string>
     <string name="item_removed" msgid="851119963877842327">"Item removed"</string>
+    <string name="undo" msgid="4151576204245173321">"Undo"</string>
     <string name="action_move" msgid="4339390619886385032">"Move item"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Move to row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Move to position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Notifications and apps are off"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Close"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Closed"</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 368e4ba..d10c84b 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,9 +65,9 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Esta es una aplicación del sistema y no se puede desinstalar."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Carpeta sin nombre"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Se inhabilitó <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> tiene <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificaciones</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> tiene <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notificación</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> tiene <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificaciones</item>
+      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> tiene <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notificación</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"Página %1$d de %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Pantalla principal %1$d de %2$d"</string>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>"</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 página principal"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"El administrador inhabilitó esta función"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Permitir la rotación de la pantalla principal"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Al girar el teléfono"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Puntos de notificación"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Activada"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Desactivada"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Puntos de notificación"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Activado"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Desactivado"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Se necesita acceso a las notificaciones"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Para mostrar los puntos de notificación, activa las notificaciones de la app para <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Cambiar la configuración"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Mostrar puntos de notificación"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Mostrar puntos de notificación"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Agregar ícono a la pantalla principal"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Para nuevas apps"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Cambiar forma de los íconos"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"en la pantalla principal"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Usar valores predeterminados del sistema"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Cuadrado"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Cuadrado con esquinas redondeadas"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Círculo"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Gota"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Aplicando cambio en la forma de los íconos"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Desconocido"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Eliminar"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Buscar"</string>
@@ -114,6 +108,7 @@
     <string name="action_move_here" msgid="2170188780612570250">"Mover elemento aquí"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Se agregó el elemento a la pantalla principal."</string>
     <string name="item_removed" msgid="851119963877842327">"Se eliminó el elemento."</string>
+    <string name="undo" msgid="4151576204245173321">"Deshacer"</string>
     <string name="action_move" msgid="4339390619886385032">"Mover elemento"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Mover a fila <xliff:g id="NUMBER_0">%1$s</xliff:g>, columna <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Mover a la posición número <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Las notificaciones y las apps están desactivadas"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Cerrar"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Cerrado"</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 0829533..09b1239 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,7 +65,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Esta aplicación es del sistema y no se puede desinstalar."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Carpeta sin nombre"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Se ha inhabilitado <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> tiene <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificaciones</item>
       <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> tiene <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notificación</item>
     </plurals>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>"</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>
-    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Inhabilitada por el administrador"</string>
+    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Inhabilitado por el administrador"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Permitir rotación de la pantalla de inicio"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Al girar el teléfono"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Burbujas de notificación"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Activado"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Desactivado"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Puntos de notificación"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Activados"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Desactivadas"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Se necesita acceso a las notificaciones"</string>
-    <string name="msg_missing_notification_access" msgid="281113995110910548">"Para mostrar burbujas de notificación, activa las notificaciones de <xliff:g id="NAME">%1$s</xliff:g>"</string>
+    <string name="msg_missing_notification_access" msgid="281113995110910548">"Para mostrar puntos de notificación, activa las notificaciones de <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Cambiar ajustes"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Mostrar burbujas de notificación"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Mostrar puntos de notificación"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Añadir icono a la pantalla de inicio"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Para aplicaciones nuevas"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Cambiar forma de los iconos"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"en la pantalla de inicio"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Usar opción predeterminada del sistema"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Cuadrado"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Cuadrado con esquinas redondeadas"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Círculo"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Lágrima"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Aplicando cambios en la forma de los iconos"</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>
@@ -114,6 +108,7 @@
     <string name="action_move_here" msgid="2170188780612570250">"Mover elemento aquí"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Elemento añadido a la pantalla de inicio"</string>
     <string name="item_removed" msgid="851119963877842327">"Elemento eliminado"</string>
+    <string name="undo" msgid="4151576204245173321">"Deshacer"</string>
     <string name="action_move" msgid="4339390619886385032">"Mover elemento"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Mover a la fila <xliff:g id="NUMBER_0">%1$s</xliff:g>, columna <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Mover a la posición número <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
@@ -133,7 +128,7 @@
     <string name="widget_resized" msgid="9130327887929620">"Se ha modificado el tamaño del widget a <xliff:g id="NUMBER_0">%1$s</xliff:g> de ancho y <xliff:g id="NUMBER_1">%2$s</xliff:g> de alto"</string>
     <string name="action_deep_shortcut" msgid="2864038805849372848">"Accesos directos"</string>
     <string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Accesos directos y notificaciones"</string>
-    <string name="action_dismiss_notification" msgid="5909461085055959187">"Ignorar"</string>
+    <string name="action_dismiss_notification" msgid="5909461085055959187">"Cerrar"</string>
     <string name="notification_dismissed" msgid="6002233469409822874">"Notificación ignorada"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"Trabajo"</string>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Las notificaciones y las aplicaciones están desactivadas"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Cerrar"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Cerrada"</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-rEE/strings.xml b/res/values-et-rEE/strings.xml
deleted file mode 100644
index 1bde8aa..0000000
--- a/res/values-et-rEE/strings.xml
+++ /dev/null
@@ -1,146 +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">
-    <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
-    <string name="folder_name" msgid="7371454440695724752"></string>
-    <string name="work_folder_name" msgid="3753320833950115786">"Töö"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"Rakendus pole installitud."</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"Rakendus ei ole saadaval"</string>
-    <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">"Avaekraan"</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="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>
-    <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="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="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">"Avaekraan"</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">"Rakenduse teave"</string>
-    <string name="install_drop_target_label" msgid="2539096853673231757">"Installimine"</string>
-    <string name="permlab_install_shortcut" msgid="5632423390354674437">"installi otseteed"</string>
-    <string name="permdesc_install_shortcut" msgid="923466509822011139">"Võimaldab rakendusel lisada otseteid kasutaja sekkumiseta."</string>
-    <string name="permlab_read_settings" msgid="1941457408239617576">"loe avaekraani seadeid ja otseteid"</string>
-    <string name="permdesc_read_settings" msgid="5833423719057558387">"Võimaldab rakendusel lugeda avaekraanil seadeid ja otseteid."</string>
-    <string name="permlab_write_settings" msgid="3574213698004620587">"kirjuta avaekraani seaded ja otseteed"</string>
-    <string name="permdesc_write_settings" msgid="5440712911516509985">"Võimaldab rakendusel muuta avaekraanil seadeid ja otseteid."</string>
-    <string name="msg_no_phone_permission" msgid="9208659281529857371">"Rakendusel <xliff:g id="APP_NAME">%1$s</xliff:g> pole lubatud helistada"</string>
-    <string name="gadget_error_text" msgid="6081085226050792095">"Probleem vidina laadimisel"</string>
-    <string name="gadget_setup_text" msgid="8274003207686040488">"Seadistamine"</string>
-    <string name="uninstall_system_app_text" msgid="4172046090762920660">"See on süsteemirakendus ja seda ei saa desinstallida."</string>
-    <string name="folder_hint_text" msgid="6617836969016293992">"Nimetu kaust"</string>
-    <string name="disabled_app_label" msgid="6673129024321402780">"Rakendus <xliff:g id="APP_NAME">%1$s</xliff:g> on keelatud"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> märguannet</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> – <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> märguanne</item>
-    </plurals>
-    <string name="default_scroll_format" msgid="7475544710230993317">"Leht %1$d/%2$d"</string>
-    <string name="workspace_scroll_format" msgid="8458889198184077399">"Avaekraan %1$d/%2$d"</string>
-    <string name="workspace_new_page" msgid="257366611030256142">"Uus avaekraan"</string>
-    <string name="folder_opened" msgid="94695026776264709">"Kaust on avatud, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="4625795376335528256">"Puudutage kausta sulgemiseks"</string>
-    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Puudutage ümbernimetamise salvestamiseks"</string>
-    <string name="folder_closed" msgid="4100806530910930934">"Kaust on suletud"</string>
-    <string name="folder_renamed" msgid="1794088362165669656">"Kausta uus nimi: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="folder_name_format" msgid="6629239338071103179">"Kaust: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Vidinad"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"Taustapildid"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Avaekraani seaded"</string>
-    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Keelas administraator"</string>
-    <string name="allow_rotation_title" msgid="7728578836261442095">"Luba avaekraani pööramine"</string>
-    <string name="allow_rotation_desc" msgid="8662546029078692509">"Kui telefoni pööratakse"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Märguandetäpid"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Sees"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Väljas"</string>
-    <string name="title_missing_notification_access" msgid="7503287056163941064">"Vaja on juurdepääsu märguannetele"</string>
-    <string name="msg_missing_notification_access" msgid="281113995110910548">"Märguandetäppide kuvamiseks lülitage sisse rakenduse <xliff:g id="NAME">%1$s</xliff:g> märguanded"</string>
-    <string name="title_change_settings" msgid="1376365968844349552">"Seadete muutmine"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Kuva märguandetäpid"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Lisa ikoon avaekraanile"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Uute rakenduste puhul"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Ikooni kuju muutmine"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"avaekraanil"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Kasuta süsteemi vaikeseadet"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Ruut"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Ümarate nurkadega ruut"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Ring"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Tilk"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Ikooni kuju muudatuste rakendamine"</string>
-    <string name="package_state_unknown" msgid="7592128424511031410">"Teadmata"</string>
-    <string name="abandoned_clean_this" msgid="7610119707847920412">"Eemalda"</string>
-    <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_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="action_add_to_workspace" msgid="8902165848117513641">"Lisa avaekraanile"</string>
-    <string name="action_move_here" msgid="2170188780612570250">"Teisalda üksus siia"</string>
-    <string name="item_added_to_workspace" msgid="4211073925752213539">"Üksus lisati avaekraanile"</string>
-    <string name="item_removed" msgid="851119963877842327">"Üksus eemaldati"</string>
-    <string name="action_move" msgid="4339390619886385032">"Teisalda üksus"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Teisaldamine <xliff:g id="NUMBER_0">%1$s</xliff:g>. rea <xliff:g id="NUMBER_1">%2$s</xliff:g>. veergu"</string>
-    <string name="move_to_position" msgid="6750008980455459790">"Teisaldamine <xliff:g id="NUMBER">%1$s</xliff:g>. positsioonile"</string>
-    <string name="move_to_hotseat_position" msgid="6295412897075147808">"Teisaldamine lemmikute <xliff:g id="NUMBER">%1$s</xliff:g>. positsioonile"</string>
-    <string name="item_moved" msgid="4606538322571412879">"Üksus teisaldati"</string>
-    <string name="add_to_folder" msgid="9040534766770853243">"Lisamine kausta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="add_to_folder_with_app" msgid="4534929978967147231">"Lisamine kausta nimega <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="added_to_folder" msgid="4793259502305558003">"Üksus lisati kausta"</string>
-    <string name="create_folder_with" msgid="4050141361160214248">"Kausta loomine nimega <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="folder_created" msgid="6409794597405184510">"Kaust on loodud"</string>
-    <string name="action_move_to_workspace" msgid="1603837886334246317">"Teisalda avaekraanile"</string>
-    <string name="action_resize" msgid="1802976324781771067">"Muuda suurust"</string>
-    <string name="action_increase_width" msgid="8773715375078513326">"Suurenda laiust"</string>
-    <string name="action_increase_height" msgid="459390020612501122">"Suurenda kõrgust"</string>
-    <string name="action_decrease_width" msgid="1374549771083094654">"Vähenda laiust"</string>
-    <string name="action_decrease_height" msgid="282377193880900022">"Vähenda kõrgust"</string>
-    <string name="widget_resized" msgid="9130327887929620">"Vidina suurust muudeti. Laius: <xliff:g id="NUMBER_0">%1$s</xliff:g>. Kõrgus: <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
-    <string name="action_deep_shortcut" msgid="2864038805849372848">"Otseteed"</string>
-    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> otseteed rakenduse <xliff:g id="APP_NAME">%2$s</xliff:g> jaoks"</string>
-    <string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> otseteed ja <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> märguannet rakendusele <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
-    <string name="action_dismiss_notification" msgid="5909461085055959187">"Loobu"</string>
-    <string name="notification_dismissed" msgid="6002233469409822874">"Märguandest loobuti"</string>
-    <string name="all_apps_personal_tab" msgid="4190252696685155002">"Isiklik"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"Töö"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"Tööprofiil"</string>
-    <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Töörakendused leiate siit"</string>
-    <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Igal töörakendusel on märk ja teie organisatsioon tagab selle turvalisuse. Teisaldage rakendused avaekraanile, et neile oleks lihtsam juurde pääseda."</string>
-    <string name="work_mode_on_label" msgid="4781128097185272916">"Haldab teie organisatsioon"</string>
-    <string name="work_mode_off_label" msgid="3194894777601421047">"Märguanded ja rakendused on välja lülitatud"</string>
-    <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Sule"</string>
-    <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Suletud"</string>
-</resources>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 1e5caa6..1e470e1 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -27,7 +27,7 @@
     <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">"Avaekraan"</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>
@@ -39,6 +39,7 @@
     <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>
@@ -47,16 +48,16 @@
     <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">"Avaekraan"</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">"Rakenduse teave"</string>
     <string name="install_drop_target_label" msgid="2539096853673231757">"Installimine"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"installi otseteed"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Võimaldab rakendusel lisada otseteid kasutaja sekkumiseta."</string>
-    <string name="permlab_read_settings" msgid="1941457408239617576">"loe avaekraani seadeid ja otseteid"</string>
+    <string name="permlab_read_settings" msgid="1941457408239617576">"avakuva seadete ja otseteede lugemine"</string>
     <string name="permdesc_read_settings" msgid="5833423719057558387">"Võimaldab rakendusel lugeda avaekraanil seadeid ja otseteid."</string>
-    <string name="permlab_write_settings" msgid="3574213698004620587">"kirjuta avaekraani seaded ja otseteed"</string>
+    <string name="permlab_write_settings" msgid="3574213698004620587">"avakuva seadete ja otseteede kirjutamine"</string>
     <string name="permdesc_write_settings" msgid="5440712911516509985">"Võimaldab rakendusel muuta avaekraanil seadeid ja otseteid."</string>
     <string name="msg_no_phone_permission" msgid="9208659281529857371">"Rakendusel <xliff:g id="APP_NAME">%1$s</xliff:g> pole lubatud helistada"</string>
     <string name="gadget_error_text" msgid="6081085226050792095">"Probleem vidina laadimisel"</string>
@@ -64,13 +65,13 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"See on süsteemirakendus ja seda ei saa desinstallida."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Nimetu kaust"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Rakendus <xliff:g id="APP_NAME">%1$s</xliff:g> on keelatud"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> märguannet</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> – <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> märguanne</item>
+      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> märguanne</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"Leht %1$d/%2$d"</string>
-    <string name="workspace_scroll_format" msgid="8458889198184077399">"Avaekraan %1$d/%2$d"</string>
-    <string name="workspace_new_page" msgid="257366611030256142">"Uus avaekraan"</string>
+    <string name="workspace_scroll_format" msgid="8458889198184077399">"Avakuva %1$d/%2$d"</string>
+    <string name="workspace_new_page" msgid="257366611030256142">"Uus avakuva leht"</string>
     <string name="folder_opened" msgid="94695026776264709">"Kaust on avatud, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"Puudutage kausta sulgemiseks"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"Puudutage ümbernimetamise salvestamiseks"</string>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Kaust: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Vidinad"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Taustapildid"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Avaekraani seaded"</string>
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stiilid ja taustapildid"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Avakuva seaded"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Keelas administraator"</string>
-    <string name="allow_rotation_title" msgid="7728578836261442095">"Luba avaekraani pööramine"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Luba avakuva pööramine"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Kui telefoni pööratakse"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Märguandetäpid"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Sees"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Väljas"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Märguandetäpid"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Sees"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Väljas"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Vaja on juurdepääsu märguannetele"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Märguandetäppide kuvamiseks lülitage sisse rakenduse <xliff:g id="NAME">%1$s</xliff:g> märguanded"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Seadete muutmine"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Kuva märguandetäpid"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Lisa ikoon avaekraanile"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Kuva märguandetäpid"</string>
+    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Lisa ikoon avakuvasse"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Uute rakenduste puhul"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Ikooni kuju muutmine"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"avaekraanil"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Kasuta süsteemi vaikeseadet"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Ruut"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Ümarate nurkadega ruut"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Ring"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Tilk"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Ikooni kuju muudatuste rakendamine"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Teadmata"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Eemalda"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Otsing"</string>
@@ -110,10 +104,11 @@
     <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 avaekraanile"</string>
+    <string name="action_add_to_workspace" msgid="8902165848117513641">"Lisa avakuvasse"</string>
     <string name="action_move_here" msgid="2170188780612570250">"Teisalda üksus siia"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Üksus lisati avaekraanile"</string>
     <string name="item_removed" msgid="851119963877842327">"Üksus eemaldati"</string>
+    <string name="undo" msgid="4151576204245173321">"Võta tagasi"</string>
     <string name="action_move" msgid="4339390619886385032">"Teisalda üksus"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Teisaldamine <xliff:g id="NUMBER_0">%1$s</xliff:g>. rea <xliff:g id="NUMBER_1">%2$s</xliff:g>. veergu"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Teisaldamine <xliff:g id="NUMBER">%1$s</xliff:g>. positsioonile"</string>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Märguanded ja rakendused on välja lülitatud"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Sule"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Suletud"</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-rES/strings.xml b/res/values-eu-rES/strings.xml
deleted file mode 100644
index 128c6f4..0000000
--- a/res/values-eu-rES/strings.xml
+++ /dev/null
@@ -1,146 +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">
-    <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
-    <string name="folder_name" msgid="7371454440695724752"></string>
-    <string name="work_folder_name" msgid="3753320833950115786">"Lana"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"Aplikazioa instalatu gabe dago."</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"Ez dago erabilgarri aplikazioa"</string>
-    <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="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>
-    <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="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="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="install_drop_target_label" msgid="2539096853673231757">"Instalatu"</string>
-    <string name="permlab_install_shortcut" msgid="5632423390354674437">"Instalatu lasterbideak"</string>
-    <string name="permdesc_install_shortcut" msgid="923466509822011139">"Erabiltzaileak ezer egin gabe lasterbideak gehitzea baimentzen die aplikazioei."</string>
-    <string name="permlab_read_settings" msgid="1941457408239617576">"Irakurri hasierako ezarpenak eta lasterbideak"</string>
-    <string name="permdesc_read_settings" msgid="5833423719057558387">"Hasierako pantailako ezarpenak eta lasterbideak irakurtzea baimentzen die aplikazioei."</string>
-    <string name="permlab_write_settings" msgid="3574213698004620587">"Idatzi hasierako ezarpenak eta lasterbideak"</string>
-    <string name="permdesc_write_settings" msgid="5440712911516509985">"Hasierako pantailako ezarpenak eta lasterbideak aldatzea baimentzen die aplikazioei."</string>
-    <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioak ez du telefono-deiak egiteko baimenik"</string>
-    <string name="gadget_error_text" msgid="6081085226050792095">"Arazo bat izan da widgeta kargatzean"</string>
-    <string name="gadget_setup_text" msgid="8274003207686040488">"Konfigurazioa"</string>
-    <string name="uninstall_system_app_text" msgid="4172046090762920660">"Sistema-aplikazioa da hau eta ezin da desinstalatu."</string>
-    <string name="folder_hint_text" msgid="6617836969016293992">"Izenik gabeko karpeta"</string>
-    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> desgaituta dago"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> jakinarazpen dauzka <xliff:g id="APP_NAME_2">%1$s</xliff:g> aplikazioak</item>
-      <item quantity="one"><xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> jakinarazpen dauka <xliff:g id="APP_NAME_0">%1$s</xliff:g> aplikazioak</item>
-    </plurals>
-    <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_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" msgid="6629239338071103179">"Karpeta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Widgetak"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"Horma-paperak"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Hasierako pantailaren ezarpenak"</string>
-    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administratzaileak desgaitu du"</string>
-    <string name="allow_rotation_title" msgid="7728578836261442095">"Baimendu hasierako pantaila biratzea"</string>
-    <string name="allow_rotation_desc" msgid="8662546029078692509">"Telefonoa biratzen denean"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Jakinarazpen-biribiltxoak"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Aktibatuta"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Desaktibatuta"</string>
-    <string name="title_missing_notification_access" msgid="7503287056163941064">"Jakinarazpenetarako sarbidea behar da"</string>
-    <string name="msg_missing_notification_access" msgid="281113995110910548">"Jakinarazpen-biribiltxoak ikusteko, aktibatu <xliff:g id="NAME">%1$s</xliff:g> aplikazioaren jakinarazpenak"</string>
-    <string name="title_change_settings" msgid="1376365968844349552">"Aldatu ezarpenak"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Erakutsi jakinarazpen-biribiltxoak"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Gehitu ikonoa hasierako pantailan"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Aplikazio berrietan"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Aldatu ikonoaren forma"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"Hasierako pantailan"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Erabili sistemaren balio lehenetsiak"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Karratua"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Ertz biribilduko karratua"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Zirkulua"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Malkoa"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Ikonoaren forman egindako aldaketak aplikatzen"</string>
-    <string name="package_state_unknown" msgid="7592128424511031410">"Ezezaguna"</string>
-    <string name="abandoned_clean_this" msgid="7610119707847920412">"Kendu"</string>
-    <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_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="action_add_to_workspace" msgid="8902165848117513641">"Gehitu hasierako pantailan"</string>
-    <string name="action_move_here" msgid="2170188780612570250">"Ekarri elementua hona"</string>
-    <string name="item_added_to_workspace" msgid="4211073925752213539">"Gehitu da elementua hasierako pantailan"</string>
-    <string name="item_removed" msgid="851119963877842327">"Kendu da elementua"</string>
-    <string name="action_move" msgid="4339390619886385032">"Mugitu elementua"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Eraman <xliff:g id="NUMBER_0">%1$s</xliff:g>. errenkadara, <xliff:g id="NUMBER_1">%2$s</xliff:g>. zutabera"</string>
-    <string name="move_to_position" msgid="6750008980455459790">"Eraman <xliff:g id="NUMBER">%1$s</xliff:g>. postura"</string>
-    <string name="move_to_hotseat_position" msgid="6295412897075147808">"Eraman gogokoen <xliff:g id="NUMBER">%1$s</xliff:g>. postura"</string>
-    <string name="item_moved" msgid="4606538322571412879">"Elementua mugitu da"</string>
-    <string name="add_to_folder" msgid="9040534766770853243">"Gehitu <xliff:g id="NAME">%1$s</xliff:g> karpetan"</string>
-    <string name="add_to_folder_with_app" msgid="4534929978967147231">"Gehitu <xliff:g id="NAME">%1$s</xliff:g> duen karpetan"</string>
-    <string name="added_to_folder" msgid="4793259502305558003">"Elementua karpetan gehitu da"</string>
-    <string name="create_folder_with" msgid="4050141361160214248">"Sortu karpeta <xliff:g id="NAME">%1$s</xliff:g> elementuarekin"</string>
-    <string name="folder_created" msgid="6409794597405184510">"Karpeta sortu da"</string>
-    <string name="action_move_to_workspace" msgid="1603837886334246317">"Eraman hasierako pantailara"</string>
-    <string name="action_resize" msgid="1802976324781771067">"Aldatu tamaina"</string>
-    <string name="action_increase_width" msgid="8773715375078513326">"Handitu zabalera"</string>
-    <string name="action_increase_height" msgid="459390020612501122">"Handitu altuera"</string>
-    <string name="action_decrease_width" msgid="1374549771083094654">"Txikitu zabalera"</string>
-    <string name="action_decrease_height" msgid="282377193880900022">"Txikitu altuera"</string>
-    <string name="widget_resized" msgid="9130327887929620">"Aldatu da widgetaren tamaina. Zabalera: <xliff:g id="NUMBER_0">%1$s</xliff:g>. Altuera: <xliff:g id="NUMBER_1">%2$s</xliff:g>."</string>
-    <string name="action_deep_shortcut" msgid="2864038805849372848">"Lasterbideak"</string>
-    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> aplikazioaren <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> lasterbide"</string>
-    <string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> aplikazioaren <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> lasterbide eta <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> jakinarazpen"</string>
-    <string name="action_dismiss_notification" msgid="5909461085055959187">"Baztertu"</string>
-    <string name="notification_dismissed" msgid="6002233469409822874">"Baztertu egin da jakinarazpena"</string>
-    <string name="all_apps_personal_tab" msgid="4190252696685155002">"Pertsonalak"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"Lanekoak"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"Laneko profila"</string>
-    <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Hemen dituzu laneko aplikazioak"</string>
-    <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Laneko aplikazio bakoitzak bereizgarri bat dauka eta erakundeak babesten du. Aplikazioak errazago atzitzeko, eraman itzazu hasierako pantailara."</string>
-    <string name="work_mode_on_label" msgid="4781128097185272916">"Erakundeak kudeatzen du"</string>
-    <string name="work_mode_off_label" msgid="3194894777601421047">"Jakinarazpenak eta aplikazioak desaktibatuta daude"</string>
-    <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Itxi"</string>
-    <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Itxita"</string>
-</resources>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index b69228a..9ba46c1 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -53,20 +54,20 @@
     <string name="app_info_drop_target_label" msgid="692894985365717661">"Aplikazioaren datuak"</string>
     <string name="install_drop_target_label" msgid="2539096853673231757">"Instalatu"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"Instalatu lasterbideak"</string>
-    <string name="permdesc_install_shortcut" msgid="923466509822011139">"Erabiltzaileak ezer egin gabe lasterbideak gehitzea baimentzen die aplikazioei."</string>
+    <string name="permdesc_install_shortcut" msgid="923466509822011139">"Erabiltzaileak ezer egin gabe lasterbideak gehitzeko baimena ematen die aplikazioei."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"Irakurri hasierako ezarpenak eta lasterbideak"</string>
-    <string name="permdesc_read_settings" msgid="5833423719057558387">"Hasierako pantailako ezarpenak eta lasterbideak irakurtzea baimentzen die aplikazioei."</string>
+    <string name="permdesc_read_settings" msgid="5833423719057558387">"Hasierako pantailako ezarpenak eta lasterbideak irakurtzeko baimena ematen die aplikazioei."</string>
     <string name="permlab_write_settings" msgid="3574213698004620587">"Idatzi hasierako ezarpenak eta lasterbideak"</string>
-    <string name="permdesc_write_settings" msgid="5440712911516509985">"Hasierako pantailako ezarpenak eta lasterbideak aldatzea baimentzen die aplikazioei."</string>
+    <string name="permdesc_write_settings" msgid="5440712911516509985">"Hasierako pantailako ezarpenak eta lasterbideak aldatzeko baimena ematen die aplikazioei."</string>
     <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioak ez du telefono-deiak egiteko baimenik"</string>
     <string name="gadget_error_text" msgid="6081085226050792095">"Arazo bat izan da widgeta kargatzean"</string>
     <string name="gadget_setup_text" msgid="8274003207686040488">"Konfigurazioa"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Sistema-aplikazioa da hau eta ezin da desinstalatu."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Izenik gabeko karpeta"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> desgaituta dago"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> jakinarazpen dauzka <xliff:g id="APP_NAME_2">%1$s</xliff:g> aplikazioak</item>
-      <item quantity="one"><xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> jakinarazpen dauka <xliff:g id="APP_NAME_0">%1$s</xliff:g> aplikazioak</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> aplikazioak <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> jakinarazpen ditu</item>
+      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> aplikazioak <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> jakinarazpen du</item>
     </plurals>
     <string name="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>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Karpeta: <xliff:g id="NAME">%1$s</xliff:g>"</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>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administratzaileak desgaitu du"</string>
-    <string name="allow_rotation_title" msgid="7728578836261442095">"Baimendu hasierako pantaila biratzea"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Eman hasierako pantaila biratzeko baimena"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Telefonoa biratzen denean"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Jakinarazpen-biribiltxoak"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Aktibatuta"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Desaktibatuta"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Jakinarazpen-biribiltxoak"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Aktibatuta"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Desaktibatuta"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Jakinarazpenetarako sarbidea behar da"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Jakinarazpen-biribiltxoak ikusteko, aktibatu <xliff:g id="NAME">%1$s</xliff:g> aplikazioaren jakinarazpenak"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Aldatu ezarpenak"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Erakutsi jakinarazpen-biribiltxoak"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Erakutsi jakinarazpen-biribiltxoak"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Gehitu ikonoa hasierako pantailan"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Aplikazio berrietan"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Aldatu ikonoaren forma"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"Hasierako pantailan"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Erabili sistemaren balio lehenetsiak"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Karratua"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Ertz biribilduko karratua"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Zirkulua"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Malkoa"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Ikonoaren forman egindako aldaketak aplikatzen"</string>
+    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Aplikazio berrien kasuan"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Ezezaguna"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Kendu"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Bilatu"</string>
@@ -114,6 +108,7 @@
     <string name="action_move_here" msgid="2170188780612570250">"Ekarri elementua hona"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Gehitu da elementua hasierako pantailan"</string>
     <string name="item_removed" msgid="851119963877842327">"Kendu da elementua"</string>
+    <string name="undo" msgid="4151576204245173321">"Desegin"</string>
     <string name="action_move" msgid="4339390619886385032">"Mugitu elementua"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Eraman <xliff:g id="NUMBER_0">%1$s</xliff:g>. errenkadara, <xliff:g id="NUMBER_1">%2$s</xliff:g>. zutabera"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Eraman <xliff:g id="NUMBER">%1$s</xliff:g>. postura"</string>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Jakinarazpenak eta aplikazioak desaktibatuta daude"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Itxi"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Itxita"</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 aa06188..926cdb9 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -33,12 +33,13 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"برای انتخاب یک ابزارک، دو ضربه سریع بزنید و نگه‌دارید یا از کنش‌های سفارشی استفاده کنید."</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="add_item_request_drag_hint" msgid="5899764264480397019">"آن را لمس کنید و بکشید تا به‌صورت دستی اضافه شود"</string>
     <string name="place_automatically" msgid="8064208734425456485">"افزودن خودکار"</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>
@@ -64,9 +65,9 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"این برنامه سیستمی است و حذف نصب نمی‌شود."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"پوشه بی‌نام"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> غیرفعال شد"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> اعلان دارد</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> اعلان دارد</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>، <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> اعلان دارد</item>
+      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>، <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> اعلان دارد</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"‏صفحه %1$d از %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"‏صفحه اصلی %1$d از %2$d"</string>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"پوشه: <xliff:g id="NAME">%1$s</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="icon_badging_title" msgid="874121399231955394">"نقطه‌های اعلان"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"روشن"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"خاموش"</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="icon_badging_service_title" msgid="2309733118428242174">"نمایش نقطه‌های اعلان"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"نمایش نقطه‌های اعلان"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"افزودن نماد به صفحه اصلی"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"برای برنامه‌های جدید"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"تغییر شکل نماد"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"در صفحه اصلی"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"استفاده از پیش‌فرض سیستم"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"مربع"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"مربع با گوشه‌های گرد"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"دایره"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"قطره اشک"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"درحال اعمال کردن تغییرات شکل نماد"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"نامشخص"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"حذف"</string>
     <string name="abandoned_search" msgid="891119232568284442">"جستجو"</string>
@@ -114,6 +108,7 @@
     <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>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"اعلان‌ها و برنامه‌ها خاموش هستند"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"بستن"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"بسته‌شده"</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 4b361ac..f87441f 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,7 +65,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Tämä on järjestelmäsovellus, eikä sitä voi poistaa."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Nimetön kansio"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> poistettiin käytöstä"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>: <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ilmoitusta</item>
       <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>: <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> ilmoitus</item>
     </plurals>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Kansio: <xliff:g id="NAME">%1$s</xliff:g>"</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">"Kotiasetukset"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Järjestelmänvalvoja on poistanut toiminnon käytöstä."</string>
     <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="icon_badging_title" msgid="874121399231955394">"Pistemerkit"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Käytössä"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Pois käytöstä"</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_off" msgid="1760796511504341095">"Ei käytössä"</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>
     <string name="title_change_settings" msgid="1376365968844349552">"Muuta asetuksia"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Näytä ilmoituksista kertovat pistemerkit"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Näytä ilmoituksista kertovat pistemerkit"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Lisää kuvake aloitusruutuun"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Uusille sovelluksille"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Muuta kuvakkeen muotoa"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"aloitusnäytöllä"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Käytä järjestelmän oletusarvoa"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Neliö"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Ympyräneliö"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Ympyrä"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Pisara"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Sovelletaan kuvakkeiden muotojen muutoksia"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Tuntematon"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Poista"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Haku"</string>
@@ -114,6 +108,7 @@
     <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="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>
     <string name="move_to_position" msgid="6750008980455459790">"Siirrä kohtaan <xliff:g id="NUMBER">%1$s</xliff:g>."</string>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Ilmoitukset ja sovellukset ovat poissa käytöstä"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Sulje"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Suljettu"</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 29b003e..5ac514d 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -50,7 +51,7 @@
     <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 de l\'appli"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Détails de l\'application"</string>
     <string name="install_drop_target_label" msgid="2539096853673231757">"Installer"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"installer des raccourcis"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permet à une application d\'ajouter des raccourcis sans l\'intervention de l\'utilisateur."</string>
@@ -64,9 +65,9 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Impossible de désinstaller cette application, car il s\'agit d\'une application système."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Dossier sans nom"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"L\'application <xliff:g id="APP_NAME">%1$s</xliff:g> est désactivée"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> a <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notification</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> a <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifications</item>
+      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> a <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificatio​ns</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"Page %1$d sur %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Écran d\'accueil %1$d sur %2$d"</string>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Dossier : <xliff:g id="NAME">%1$s</xliff:g>"</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="msg_disabled_by_admin" msgid="6898038085516271325">"Cette fonction est désactivée 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>
-    <string name="icon_badging_title" msgid="874121399231955394">"Points de notification"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Activé"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Désactivé"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Points de notification"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Activé"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Désactivé"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"L\'accès aux notifications est requis"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Pour afficher les points de notification, activez les notifications d\'application pour <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Modifier les paramètres"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Afficher les points de notification"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Afficher les points de notification"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Ajouter l\'icône à l\'écran d\'accueil"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Pour les nouvelles applications"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Modifier la forme de l\'icône"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"sur l\'écran d\'accueil"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Utiliser les valeurs système par défaut"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Carré"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Carré aux coins ronds"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Cercle"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Goutte"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Application des changements à la forme de l\'icône en cours…"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Inconnu"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Supprimer"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Rechercher"</string>
@@ -114,6 +108,7 @@
     <string name="action_move_here" msgid="2170188780612570250">"Déplacer l\'élément ici"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Élément ajouté à l\'écran d\'accueil"</string>
     <string name="item_removed" msgid="851119963877842327">"Élément supprimé"</string>
+    <string name="undo" msgid="4151576204245173321">"Annuler"</string>
     <string name="action_move" msgid="4339390619886385032">"Déplacer l\'élément"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Déplacer vers rangée <xliff:g id="NUMBER_0">%1$s</xliff:g> colonne <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Déplacer vers la position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Les notifications et les applications sont désactivées"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Fermer"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Fermé"</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 d47ab42..7b4bcb1 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -33,12 +33,13 @@
     <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="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="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>
     <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>
@@ -64,7 +65,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Impossible de désinstaller cette application, car il s\'agit d\'une application système."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Dossier sans nom"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> est désactivé."</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> comporte <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notification</item>
       <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> comporte <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifications</item>
     </plurals>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Dossier \"<xliff:g id="NAME">%1$s</xliff:g>\""</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fonds d\'écran"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Paramètres accueil"</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\'écran d\'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>
-    <string name="icon_badging_title" msgid="874121399231955394">"Pastilles de notification"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Activé"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Désactivé"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Pastilles de notification"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Activées"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Désactivées"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Accès aux notifications requis"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Pour afficher les pastilles de notification, activez les notifications de l\'application <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Modifier les paramètres"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Afficher les pastilles de notification"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Afficher les pastilles de notification"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Ajouter l\'icône à l\'écran d\'accueil"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Pour les nouvelles applications"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Modifier la forme des icônes"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"sur l\'écran d\'accueil"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Utiliser la valeur système par défaut"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Carré"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Squircle"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Cercle"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Goutte"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Application des modifications de forme des icônes…"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Inconnu"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Supprimer"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Rechercher"</string>
@@ -114,6 +108,7 @@
     <string name="action_move_here" msgid="2170188780612570250">"Déplacer l\'élément ici"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"L\'élément a bien été ajouté à l\'écran d\'accueil."</string>
     <string name="item_removed" msgid="851119963877842327">"L\'élément a bien été supprimé."</string>
+    <string name="undo" msgid="4151576204245173321">"Annuler"</string>
     <string name="action_move" msgid="4339390619886385032">"Déplacer l\'élément"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Déplacer vers la ligne <xliff:g id="NUMBER_0">%1$s</xliff:g>, colonne <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Déplacer vers la position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Les notifications et les applications sont désactivées"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Fermer"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Fermé"</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-rES/strings.xml b/res/values-gl-rES/strings.xml
deleted file mode 100644
index 1efa5ce..0000000
--- a/res/values-gl-rES/strings.xml
+++ /dev/null
@@ -1,146 +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">
-    <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
-    <string name="folder_name" msgid="7371454440695724752"></string>
-    <string name="work_folder_name" msgid="3753320833950115786">"Traballo"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"A aplicación non está instalada"</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"A aplicación non está dispoñible"</string>
-    <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="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>
-    <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="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="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="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>
-    <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar atallos"</string>
-    <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite a unha aplicación engadir atallos sen intervención do usuario."</string>
-    <string name="permlab_read_settings" msgid="1941457408239617576">"ler a configuración e os atallos da pantalla de inicio"</string>
-    <string name="permdesc_read_settings" msgid="5833423719057558387">"Permite a unha aplicación ler a configuración e os atallos da páxina de inicio."</string>
-    <string name="permlab_write_settings" msgid="3574213698004620587">"modificar a configuración e os atallos da pantalla de inicio"</string>
-    <string name="permdesc_write_settings" msgid="5440712911516509985">"Permite a unha aplicación cambiar a configuración e os atallos da pantalla de inicio."</string>
-    <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> non ten permiso para facer chamadas telefónicas"</string>
-    <string name="gadget_error_text" msgid="6081085226050792095">"Produciuse un problema ao cargar o widget"</string>
-    <string name="gadget_setup_text" msgid="8274003207686040488">"Configuración"</string>
-    <string name="uninstall_system_app_text" msgid="4172046090762920660">"Esta aplicación é do sistema e non se pode desinstalar."</string>
-    <string name="folder_hint_text" msgid="6617836969016293992">"Cartafol sen nome"</string>
-    <string name="disabled_app_label" msgid="6673129024321402780">"Desactivouse <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ten <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificacións</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, ten <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notificación</item>
-    </plurals>
-    <string name="default_scroll_format" msgid="7475544710230993317">"Páxina %1$d de %2$d"</string>
-    <string name="workspace_scroll_format" msgid="8458889198184077399">"Pantalla de inicio %1$d de %2$d"</string>
-    <string name="workspace_new_page" msgid="257366611030256142">"Nova páxina da pantalla de inicio"</string>
-    <string name="folder_opened" msgid="94695026776264709">"Abriuse o cartafol, <xliff:g id="WIDTH">%1$d</xliff:g> por <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="4625795376335528256">"Toca fóra para pechar o cartafol"</string>
-    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Toca fóra para cambiar o nome do cartafol"</string>
-    <string name="folder_closed" msgid="4100806530910930934">"Pechouse o cartafol"</string>
-    <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" msgid="6629239338071103179">"Cartafol: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Configuración da pantalla de Inicio"</string>
-    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Función desactivada polo administrador"</string>
-    <string name="allow_rotation_title" msgid="7728578836261442095">"Permitir xirar a pantalla de inicio"</string>
-    <string name="allow_rotation_desc" msgid="8662546029078692509">"Ao xirar o teléfono"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Puntos de notificacións"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Activado"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Desactivado"</string>
-    <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="icon_badging_service_title" msgid="2309733118428242174">"Mostrar puntos de notificación"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Engadir icona á pantalla de inicio"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Para novas aplicacións"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Cambiar forma das iconas"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"na pantalla de inicio"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Usar valores predeterminados do sistema"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Cadrado"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Cadrado de bordos redondeados"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Círculo"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Bágoa"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Aplicando cambios na forma das iconas"</string>
-    <string name="package_state_unknown" msgid="7592128424511031410">"Descoñecido"</string>
-    <string name="abandoned_clean_this" msgid="7610119707847920412">"Eliminar"</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="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="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="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>
-    <string name="move_to_position" msgid="6750008980455459790">"Mover á posición <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
-    <string name="move_to_hotseat_position" msgid="6295412897075147808">"Mover á posición dos favoritos <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
-    <string name="item_moved" msgid="4606538322571412879">"Moveuse o elemento"</string>
-    <string name="add_to_folder" msgid="9040534766770853243">"Engadir ao cartafol: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="add_to_folder_with_app" msgid="4534929978967147231">"Engadir ao cartafol con <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="added_to_folder" msgid="4793259502305558003">"Engadiuse o elemento ao cartafol"</string>
-    <string name="create_folder_with" msgid="4050141361160214248">"Crear cartafol con: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="folder_created" msgid="6409794597405184510">"Creouse o cartafol"</string>
-    <string name="action_move_to_workspace" msgid="1603837886334246317">"Mover á pantalla de inicio"</string>
-    <string name="action_resize" msgid="1802976324781771067">"Cambiar tamaño"</string>
-    <string name="action_increase_width" msgid="8773715375078513326">"Aumentar ancho"</string>
-    <string name="action_increase_height" msgid="459390020612501122">"Aumentar altura"</string>
-    <string name="action_decrease_width" msgid="1374549771083094654">"Reducir ancho"</string>
-    <string name="action_decrease_height" msgid="282377193880900022">"Reducir altura"</string>
-    <string name="widget_resized" msgid="9130327887929620">"Cambiouse o tamaño do widget polo ancho <xliff:g id="NUMBER_0">%1$s</xliff:g> e a altura <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
-    <string name="action_deep_shortcut" msgid="2864038805849372848">"Atallos"</string>
-    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> atallos para <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
-    <string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> atallos e <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> notificacións para a aplicación <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
-    <string name="action_dismiss_notification" msgid="5909461085055959187">"Ignorar"</string>
-    <string name="notification_dismissed" msgid="6002233469409822874">"Ignorouse a notificación"</string>
-    <string name="all_apps_personal_tab" msgid="4190252696685155002">"Persoal"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"Traballo"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil de traballo"</string>
-    <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Buscar aplicacións do traballo aquí"</string>
-    <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"As aplicacións do traballo teñen unha insignia e están protexidas pola túa organización. Traslada as aplicacións á pantalla de inicio para acceder a elas de forma máis fácil."</string>
-    <string name="work_mode_on_label" msgid="4781128097185272916">"Perfil xestionado pola túa organización"</string>
-    <string name="work_mode_off_label" msgid="3194894777601421047">"As notificacións e as aplicacións están desactivadas"</string>
-    <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Pechar"</string>
-    <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Pechada"</string>
-</resources>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index b94d693..e115a72 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,9 +65,9 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Esta aplicación é do sistema e non se pode desinstalar."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Cartafol sen nome"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Desactivouse <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ten <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificacións</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, ten <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notificación</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="other">A aplicación <xliff:g id="APP_NAME_2">%1$s</xliff:g> ten <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificacións</item>
+      <item quantity="one">A aplicación <xliff:g id="APP_NAME_0">%1$s</xliff:g> ten <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notificación</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"Páxina %1$d de %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Pantalla de inicio %1$d de %2$d"</string>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Cartafol: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Configuración da pantalla de Inicio"</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>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Función desactivada polo administrador"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Permitir xirar a pantalla de inicio"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Ao xirar o teléfono"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Puntos de notificacións"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Activado"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Desactivado"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Puntos de notificacións"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Activados"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Desactivados"</string>
     <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="icon_badging_service_title" msgid="2309733118428242174">"Mostrar puntos de notificación"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Mostrar puntos de notificacións"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Engadir icona á pantalla de inicio"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Para novas aplicacións"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Cambiar forma das iconas"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"na pantalla de inicio"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Usar valores predeterminados do sistema"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Cadrado"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Cadrado de bordos redondeados"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Círculo"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Bágoa"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Aplicando cambios na forma das iconas"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Descoñecido"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Eliminar"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Buscar"</string>
@@ -114,6 +108,7 @@
     <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="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>
     <string name="move_to_position" msgid="6750008980455459790">"Mover á posición <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"As notificacións e as aplicacións están desactivadas"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Pechar"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Pechada"</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-rIN/strings.xml b/res/values-gu-rIN/strings.xml
deleted file mode 100644
index 234fada..0000000
--- a/res/values-gu-rIN/strings.xml
+++ /dev/null
@@ -1,146 +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">
-    <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
-    <string name="folder_name" msgid="7371454440695724752"></string>
-    <string name="work_folder_name" msgid="3753320833950115786">"કાર્યાલય"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"ઍપ્લિકેશન ઇન્સ્ટોલ થઈ નથી."</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"ઍપ્લિકેશન ઉપલબ્ધ નથી"</string>
-    <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="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="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="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="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="permlab_install_shortcut" msgid="5632423390354674437">"શોર્ટકટ્સ ઇન્સ્ટોલ કરો"</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="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>
-    <string name="gadget_setup_text" msgid="8274003207686040488">"સેટઅપ"</string>
-    <string name="uninstall_system_app_text" msgid="4172046090762920660">"આ એક સિસ્ટમ ઍપ્લિકેશન છે અને અનઇન્સ્ટોલ કરી શકાતી નથી."</string>
-    <string name="folder_hint_text" msgid="6617836969016293992">"અનામી ફોલ્ડર"</string>
-    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> અક્ષમ કરી"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>ના <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> નોટિફિકેશન છે</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>ના <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> નોટિફિકેશન છે</item>
-    </plurals>
-    <string name="default_scroll_format" msgid="7475544710230993317">"%2$d માંથી %1$d પૃષ્ઠ"</string>
-    <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d માંથી %1$d હોમ સ્ક્રીન"</string>
-    <string name="workspace_new_page" msgid="257366611030256142">"નવું હોમ સ્ક્રીન પૃષ્ઠ"</string>
-    <string name="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" msgid="6629239338071103179">"ફોલ્ડર: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"વિજેટ્સ"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"વૉલપેપર્સ"</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="icon_badging_title" msgid="874121399231955394">"સૂચના બિંદુઓ"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"ચાલુ"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"બંધ"</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="icon_badging_service_title" msgid="2309733118428242174">"નોટિફિકેશન માટેનું ચિહ્ન બતાવો"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"હોમ સ્ક્રીન પર આઇકન ઉમેરો"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"નવી ઍપ્લિકેશનો માટે"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"આઇકનનો આકાર બદલો"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"હોમ સ્ક્રીન પર"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"સિસ્ટમ ડિફૉલ્ટનો ઉપયોગ કરો"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"ચોરસ"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"ચોરસ જેવું ગોળ"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"વર્તુળ"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"ટિઅરડ્રોપ"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"આઇકનના આકારમાં કરેલ ફેરફારો લાગુ કરી રહ્યા છીએ"</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_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="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="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="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>
-    <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_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> માટે <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> શૉર્ટકટ"</string>
-    <string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> માટે <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> શૉર્ટકટ અને <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> સૂચનાઓ"</string>
-    <string name="action_dismiss_notification" msgid="5909461085055959187">"છોડી દો"</string>
-    <string name="notification_dismissed" msgid="6002233469409822874">"સૂચના છોડી દીધી"</string>
-    <string name="all_apps_personal_tab" msgid="4190252696685155002">"મનગમતી ઍપ"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"કાર્યાલયની ઍપ"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"કાર્યાલયની પ્રોફાઇલ"</string>
-    <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"કાર્ય ઍપને અહીંથી મેળવો"</string>
-    <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"દરેક કાર્ય ઍપ પાસે એક બૅજ હોય છે અને તમારી સંસ્થા દ્વારા તેને સુરક્ષિત રાખવામાં આવે છે. વધુ સરળ ઍક્સેસ માટે ઍપને તમારી હોમ સ્ક્રીન પર ખસેડો."</string>
-    <string name="work_mode_on_label" msgid="4781128097185272916">"તમારી સંસ્થા દ્વારા મેનેજ કરેલ"</string>
-    <string name="work_mode_off_label" msgid="3194894777601421047">"નોટિફિકેશન અને ઍપ બંધ છે"</string>
-    <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"બંધ કરો"</string>
-    <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"બંધ"</string>
-</resources>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index 6468d8b..7def3ac 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,7 +65,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"આ એક સિસ્ટમ ઍપ્લિકેશન છે અને અનઇન્સ્ટોલ કરી શકાતી નથી."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"અનામી ફોલ્ડર"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> અક્ષમ કરી"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>ના <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> નોટિફિકેશન છે</item>
       <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>ના <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> નોટિફિકેશન છે</item>
     </plurals>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"ફોલ્ડર: <xliff:g id="NAME">%1$s</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="icon_badging_title" msgid="874121399231955394">"સૂચના બિંદુઓ"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"ચાલુ"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"બંધ"</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="icon_badging_service_title" msgid="2309733118428242174">"નોટિફિકેશન માટેનું ચિહ્ન બતાવો"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"નોટિફિકેશન માટેના ચિહ્ન બતાવો"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"હોમ સ્ક્રીન પર આઇકન ઉમેરો"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"નવી ઍપ્લિકેશનો માટે"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"આઇકનનો આકાર બદલો"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"હોમ સ્ક્રીન પર"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"સિસ્ટમ ડિફૉલ્ટનો ઉપયોગ કરો"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"ચોરસ"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"ચોરસ જેવું ગોળ"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"વર્તુળ"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"ટિઅરડ્રોપ"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"આઇકનના આકારમાં કરેલ ફેરફારો લાગુ કરી રહ્યા છીએ"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"અજાણ્યો"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"દૂર કરો"</string>
     <string name="abandoned_search" msgid="891119232568284442">"શોધો"</string>
@@ -108,14 +102,13 @@
     <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>
-    <!-- no translation found for widgets_list (796804551140113767) -->
-    <skip />
-    <!-- no translation found for widgets_list_closed (6141506579418771922) -->
-    <skip />
+    <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="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>
@@ -146,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"નોટિફિકેશન અને ઍપ બંધ છે"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"બંધ કરો"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"બંધ"</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 1b5ce26..b796140e 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -22,9 +22,9 @@
     <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="work_folder_name" msgid="3753320833950115786">"कार्यस्‍थल"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"एप्‍लिकेशन इंस्‍टॉल नहीं है."</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"ऐप्स उपलब्ध नहीं है"</string>
-    <string name="safemode_shortcut_error" msgid="9160126848219158407">"डाउनलोड किए गए ऐप्स सुरक्षित मोड में अक्षम है"</string>
+    <string name="activity_not_found" msgid="8071924732094499514">"ऐप्‍लिकेशन इंस्‍टॉल नहीं है."</string>
+    <string name="activity_not_available" msgid="7456344436509528827">"ऐप्लिकेशन उपलब्ध नहीं है"</string>
+    <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>
@@ -39,6 +39,7 @@
     <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>
@@ -50,7 +51,7 @@
     <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="permlab_install_shortcut" msgid="5632423390354674437">"शॉर्टकट इंस्‍टॉल करें"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"ऐप को उपयोगकर्ता के हस्‍तक्षेप के बिना शॉर्टकट जोड़ने देती है."</string>
@@ -64,8 +65,8 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"यह एक सिस्टम ऐप्लिकेशन है और इसे अनइंस्टॉल नहीं किया जा सकता."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"अनामित फ़ोल्डर"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> अक्षम है"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> की <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> सूचनाएं हैं</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> की <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> सूचना है</item>
       <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> की <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> सूचनाएं हैं</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"पेज %2$d में से %1$d"</string>
@@ -77,45 +78,37 @@
     <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" msgid="6629239338071103179">"फ़ोल्डर: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"शॉर्टकट"</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="icon_badging_title" msgid="874121399231955394">"सूचना बिंदु"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"चालू"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"बंद"</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="icon_badging_service_title" msgid="2309733118428242174">"नए नोटिफ़िकेशन बताने वाला गोल निशान दिखाएं"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"नई सूचनाएं बताने वाला गोल निशान दिखाएं"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"होम स्क्रीन में आइकॉन जोड़ें"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"नए ऐप्लिकेशन के लिए"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"आइकॉन का आकार बदलें"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"होम स्‍क्रीन पर"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"सिस्टम डिफ़ॉल्ट का उपयोग करें"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"वर्ग"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"गोल कोनों वाला वर्ग"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"मंडली"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"आंसू की बूंद"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"आइकॉन के आकार में बदलाव किए जा रहे हैं"</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_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_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>
-    <!-- no translation found for widgets_list (796804551140113767) -->
-    <skip />
-    <!-- no translation found for widgets_list_closed (6141506579418771922) -->
-    <skip />
+    <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="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>
@@ -139,11 +132,12 @@
     <string name="notification_dismissed" msgid="6002233469409822874">"सूचना को खारिज किया गया"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"निजी ऐप"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"काम से जुड़े ऐप"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"कार्य प्रोफ़ाइल"</string>
+    <string name="work_profile_toggle_label" msgid="3081029915775481146">"वर्क प्रोफ़ाइल"</string>
     <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"काम से जुड़े सभी ऐप्लिकेशन यहां पाएं"</string>
     <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"काम से जुड़े हर ऐप्लिकेशन पर एक बैज (निशान) होता है और इन ऐप्लिकेशन की सुरक्षा आपका संगठन करता है. आसानी से इस्तेमाल करने के लिए ऐप्लिकेशन को अपनी होम स्क्रीन पर ले जाएं."</string>
     <string name="work_mode_on_label" msgid="4781128097185272916">"आपका संगठन प्रबंधित कर रहा है"</string>
     <string name="work_mode_off_label" msgid="3194894777601421047">"सूचनाएं और ऐप्लिकेशन बंद हैं"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"बंद करें"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"बंद कर दिया गया"</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 ea340d6..1caa27f 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,7 +65,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ovo je aplikacija sustava i ne može se ukloniti."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Neimenovana mapa"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> onemogućena"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obavijest</item>
       <item quantity="few"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obavijesti</item>
       <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obavijesti</item>
@@ -80,27 +81,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Mapa: <xliff:g id="NAME">%1$s</xliff:g>"</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>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Onemogućio administrator"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Dopusti zakretanje početnog zaslona"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Kada se telefon zakrene"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Točke obavijesti"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Uključeno"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Isključeno"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Točke obavijesti"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Uključeno"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Isključeno"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Potreban je pristup obavijestima"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Za prikaz točaka obavijesti uključite obavijesti aplikacije <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Promjena postavki"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Prikaži točke obavijesti"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Prikaži točke obavijesti"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Dodaj ikonu na početni zaslon"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Za nove aplikacije"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Promijeni oblik ikona"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"na početnom zaslonu"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Upotrijebi zadane postavke sustava"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Zaobljeni kvadrat"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Krug"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Suza"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Primjena promjena oblika ikona"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Nepoznato"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Ukloni"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Traži"</string>
@@ -115,6 +109,7 @@
     <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 zaslon"</string>
     <string name="item_removed" msgid="851119963877842327">"Stavka je uklonjena"</string>
+    <string name="undo" msgid="4151576204245173321">"Poništi"</string>
     <string name="action_move" msgid="4339390619886385032">"Premještanje stavke"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Premještanje u redak <xliff:g id="NUMBER_0">%1$s</xliff:g>, stupac <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Premještanje na položaj <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
@@ -145,4 +140,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Obavijesti i aplikacije isključeni su"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Zatvori"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Zatvoreno"</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 7b87381..3327590 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,7 +65,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ez egy rendszeralkalmazás, és nem lehet eltávolítani."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Névtelen mappa"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> letiltva"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="other">A(z) <xliff:g id="APP_NAME_2">%1$s</xliff:g> <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> értesítéssel rendelkezik</item>
       <item quantity="one">A(z) <xliff:g id="APP_NAME_0">%1$s</xliff:g> <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> értesítéssel rendelkezik</item>
     </plurals>
@@ -79,29 +80,22 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Mappa: <xliff:g id="NAME">%1$s</xliff:g>"</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őoldal beállításai"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"A rendszergazda letiltotta"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"A kezdőképernyő elforgatásának engedélyezése"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"A telefon elforgatásakor"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Értesítési pöttyök"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Bekapcsolva"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Kikapcsolva"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Értesítési pöttyök"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Be"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Ki"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Értesítésekhez való hozzáférésre van szükség"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Az értesítési pöttyök megjelenítéséhez kapcsolja be a(z) <xliff:g id="NAME">%1$s</xliff:g> alkalmazás értesítéseit"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Beállítások módosítása"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Értesítési pöttyök megjelenítése"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Értesítési pöttyök megjelenítése"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Ikon hozzáadása a kezdőképernyőhöz"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Új alkalmazásoknál"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Ikon formájának módosítása"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"a kezdőképernyőn"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Alapértelmezett érték használata"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Négyzet"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Squircle"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Kör"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Könnycsepp"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Ikonforma módosításainak alkalmazása…"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Ismeretlen"</string>
-    <string name="abandoned_clean_this" msgid="7610119707847920412">"Eltávolítás"</string>
+    <string name="abandoned_clean_this" msgid="7610119707847920412">"Törlés"</string>
     <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>
@@ -114,6 +108,7 @@
     <string name="action_move_here" msgid="2170188780612570250">"Elem áthelyezése ide"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Elem hozzáadva a kezdőképernyőhöz"</string>
     <string name="item_removed" msgid="851119963877842327">"Elem eltávolítva"</string>
+    <string name="undo" msgid="4151576204245173321">"Mégse"</string>
     <string name="action_move" msgid="4339390619886385032">"Elem mozgatása"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Áthelyezés ide: <xliff:g id="NUMBER_0">%1$s</xliff:g>. sor, <xliff:g id="NUMBER_1">%2$s</xliff:g>. oszlop"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Áthelyezés a(z) <xliff:g id="NUMBER">%1$s</xliff:g>. pozícióba"</string>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Az értesítések és az alkalmazások ki vannak kapcsolva"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Bezárás"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Bezárva"</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-rAM/strings.xml b/res/values-hy-rAM/strings.xml
deleted file mode 100644
index 6844a27..0000000
--- a/res/values-hy-rAM/strings.xml
+++ /dev/null
@@ -1,146 +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">
-    <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
-    <string name="folder_name" msgid="7371454440695724752"></string>
-    <string name="work_folder_name" msgid="3753320833950115786">"Աշխատանքային"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"Ծրագիրը տեղադրված չէ:"</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"Հավելվածը հասանելի չէ"</string>
-    <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="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="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="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="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="permlab_install_shortcut" msgid="5632423390354674437">"տեղադրել դյուրանցումներ"</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="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>
-    <string name="gadget_setup_text" msgid="8274003207686040488">"Կարգավորում"</string>
-    <string name="uninstall_system_app_text" msgid="4172046090762920660">"Սա համակարգային ծրագիր է և չի կարող ապատեղադրվել:"</string>
-    <string name="folder_hint_text" msgid="6617836969016293992">"Անանուն պանակ"</string>
-    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածն անջատված է"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ունի <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ծանուցում</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ունի <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ծանուցում</item>
-    </plurals>
-    <string name="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_name_format" msgid="6629239338071103179">"Պանակ՝ <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Վիջեթներ"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"Պաստառներ"</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="icon_badging_title" msgid="874121399231955394">"Ծանուցումների կետիկներ"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Միացված է"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Անջատված է"</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="icon_badging_service_title" msgid="2309733118428242174">"Ցուցադրել ծանուցումների կետիկները"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Ավելացնել պատկերակը Հիմնական էկրանին"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Նոր հավելվածների համար"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Փոխել պատկերակների տեսքը"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"հիմնական էկրանին"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Օգտագործել համակարգի կանխադրված կարգավորումը"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Քառակուսի"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Քառանկյուն"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Օղակ"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Արցունքաձև"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Պատկերակների տեսքի փոփոխությունները կիրառվում են"</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_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="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="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="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>
-    <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_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> դյուրանցումներ <xliff:g id="APP_NAME">%2$s</xliff:g> հավելվածի համար"</string>
-    <string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> դյուրացում և <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> ծանուցում <xliff:g id="APP_NAME">%3$s</xliff:g>-ի համար"</string>
-    <string name="action_dismiss_notification" msgid="5909461085055959187">"Անտեսել"</string>
-    <string name="notification_dismissed" msgid="6002233469409822874">"Ծանուցումը մերժված է"</string>
-    <string name="all_apps_personal_tab" msgid="4190252696685155002">"Անձնական"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"Աշխատանքային"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"Աշխատանքային պրոֆիլ"</string>
-    <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Գտեք աշխատանքային հավելվածներ այստեղ"</string>
-    <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Աշխատանքային հավելվածները նշված են հատուկ նշանով: Նման հավելվածների անվտանգությունը ապահովում է ձեր կազմակերպությունը։ Հարմարության համար աշխատանքային հավելվածները կարող եք տեղափոխել հիմնական էկրան։"</string>
-    <string name="work_mode_on_label" msgid="4781128097185272916">"Կառավարվում է ձեր կազմակերպության կողմից"</string>
-    <string name="work_mode_off_label" msgid="3194894777601421047">"Ծանուցումներն ու հավելվածներն անջատված են"</string>
-    <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Փակել"</string>
-    <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Փակվեց"</string>
-</resources>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 2b4a38b..fbb657e 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -48,7 +49,7 @@
     <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>
@@ -64,7 +65,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Սա համակարգային ծրագիր է և չի կարող ապատեղադրվել:"</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Անանուն պանակ"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածն անջատված է"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ունի <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ծանուցում</item>
       <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ունի <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ծանուցում</item>
     </plurals>
@@ -79,29 +80,22 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Պանակ՝ <xliff:g id="NAME">%1$s</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="icon_badging_title" msgid="874121399231955394">"Ծանուցումների կետիկներ"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Միացված է"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Անջատված է"</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="icon_badging_service_title" msgid="2309733118428242174">"Ցուցադրել ծանուցումների կետիկները"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Ցուցադրել ծանուցումների կետիկները"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Ավելացնել պատկերակը Հիմնական էկրանին"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Նոր հավելվածների համար"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Փոխել պատկերակների տեսքը"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"հիմնական էկրանին"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Օգտագործել համակարգի կանխադրված կարգավորումը"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Քառակուսի"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Քառանկյուն"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Օղակ"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Արցունքաձև"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Պատկերակների տեսքի փոփոխությունները կիրառվում են"</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>
@@ -114,6 +108,7 @@
     <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>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Ծանուցումներն ու հավելվածներն անջատված են"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Փակել"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Փակվեց"</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 78ffc96..3054214 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -33,12 +33,13 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Tap dua kalip &amp; tahan untuk mengambil widget atau menggunakan 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 &amp; tahan untuk menempatkan secara manual"</string>
+    <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Tap lama untuk menempatkan secara manual"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Tambahkan otomatis"</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">"Tap lama untuk memilih pintasan."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Tap dua kali &amp; tahan untuk memilih pintasan atau menggunakan tindakan khusus."</string>
@@ -64,7 +65,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ini adalah aplikasi sistem dan tidak dapat dicopot pemasangannya."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Folder Tanpa Nama"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> dinonaktifkan"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, memiliki <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifikasi</item>
       <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, memiliki <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notifikasi</item>
     </plurals>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widget"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpaper"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Setelan layar Utama"</string>
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Gaya &amp; wallpaper"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Setelan layar utama"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Dinonaktifkan oleh admin"</string>
-    <string name="allow_rotation_title" msgid="7728578836261442095">"Izinkan layar Utama diputar"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Izinkan Layar utama diputar"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Saat ponsel diputar"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Titik notifikasi"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Aktif"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Nonaktif"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Titik notifikasi"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Aktif"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Nonaktif"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Perlu akses notifikasi"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Guna menampilkan Titik Notifikasi, aktifkan notifikasi aplikasi untuk <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Ubah setelan"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Tampilkan titik notifikasi"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Tampilkan titik notifikasi"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Tambahkan ikon ke Layar utama"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Untuk aplikasi baru"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Ubah bentuk ikon"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"di layar Utama"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Gunakan default sistem"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Persegi"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Persegi bundar"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Lingkaran"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Butiran Air"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Menerapkan perubahan bentuk ikon"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Tidak dikenal"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Buang"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Telusuri"</string>
@@ -114,6 +108,7 @@
     <string name="action_move_here" msgid="2170188780612570250">"Pindahkan item ke sini"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Item ditambahkan ke layar utama"</string>
     <string name="item_removed" msgid="851119963877842327">"Item dihapus"</string>
+    <string name="undo" msgid="4151576204245173321">"Urungkan"</string>
     <string name="action_move" msgid="4339390619886385032">"Pindahkan item"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Pindahkan ke baris <xliff:g id="NUMBER_0">%1$s</xliff:g> kolom <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"PIndahkan ke posisi <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Notifikasi dan aplikasi nonaktif"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Tutup"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Ditutup"</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-rIS/strings.xml b/res/values-is-rIS/strings.xml
deleted file mode 100644
index 3b5daba..0000000
--- a/res/values-is-rIS/strings.xml
+++ /dev/null
@@ -1,146 +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">
-    <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
-    <string name="folder_name" msgid="7371454440695724752"></string>
-    <string name="work_folder_name" msgid="3753320833950115786">"Vinna"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"Forritið er ekki uppsett."</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"Forritið er ekki í boði"</string>
-    <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="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>
-    <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="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="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">"Fjarlægja"</string>
-    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Fjarlægja"</string>
-    <string name="app_info_drop_target_label" msgid="692894985365717661">"Forritsupplýsingar"</string>
-    <string name="install_drop_target_label" msgid="2539096853673231757">"Setja upp"</string>
-    <string name="permlab_install_shortcut" msgid="5632423390354674437">"setja upp flýtileiðir"</string>
-    <string name="permdesc_install_shortcut" msgid="923466509822011139">"Leyfir forriti að bæta við flýtileiðum án íhlutunar notanda."</string>
-    <string name="permlab_read_settings" msgid="1941457408239617576">"lesa stillingar og flýtileiðir heimaskjás"</string>
-    <string name="permdesc_read_settings" msgid="5833423719057558387">"Leyfir forriti að lesa stillingar og flýtileiðir heimaskjás."</string>
-    <string name="permlab_write_settings" msgid="3574213698004620587">"skrifa stillingar og flýtileiðir heimaskjás"</string>
-    <string name="permdesc_write_settings" msgid="5440712911516509985">"Leyfir forriti að breyta stillingum og flýtileiðum heimaskjás."</string>
-    <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> hefur ekki leyfi til að hringja símtöl"</string>
-    <string name="gadget_error_text" msgid="6081085226050792095">"Vandamál við að hlaða græju"</string>
-    <string name="gadget_setup_text" msgid="8274003207686040488">"Uppsetning"</string>
-    <string name="uninstall_system_app_text" msgid="4172046090762920660">"Þetta er kerfisforrit sem ekki er hægt að fjarlægja."</string>
-    <string name="folder_hint_text" msgid="6617836969016293992">"Ónefnd mappa"</string>
-    <string name="disabled_app_label" msgid="6673129024321402780">"Óvirkt <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, er með <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> tilkynningu</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, er með <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> tilkynningar</item>
-    </plurals>
-    <string name="default_scroll_format" msgid="7475544710230993317">"Síða %1$d af %2$d"</string>
-    <string name="workspace_scroll_format" msgid="8458889198184077399">"Heimaskjár %1$d af %2$d"</string>
-    <string name="workspace_new_page" msgid="257366611030256142">"Ný síða á heimaskjá"</string>
-    <string name="folder_opened" msgid="94695026776264709">"Mappa opnuð, <xliff:g id="WIDTH">%1$d</xliff:g> sinnum <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="4625795376335528256">"Ýttu til að loka möppunni"</string>
-    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Ýttu til að vista breytt heiti"</string>
-    <string name="folder_closed" msgid="4100806530910930934">"Möppu lokað"</string>
-    <string name="folder_renamed" msgid="1794088362165669656">"Heiti möppu breytt í <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="folder_name_format" msgid="6629239338071103179">"Mappa: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Græjur"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"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_desc" msgid="8662546029078692509">"Þegar símanum er snúið"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Tilkynningapunktar"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Kveikt"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Slökkt"</string>
-    <string name="title_missing_notification_access" msgid="7503287056163941064">"Aðgangs að tilkynningum er krafist"</string>
-    <string name="msg_missing_notification_access" msgid="281113995110910548">"Til að sýna tilkynningarpunkta skaltu kveikja á forritstilkynningum fyrir <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="title_change_settings" msgid="1376365968844349552">"Breyta stillingum"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Sýna tilkynningapunkta"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Bæta tákni á heimaskjáinn"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Fyrir ný forrit"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Breyta formi tákns"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"á heimaskjá"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Nota sjálfgildi kerfis"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Ferningur"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Ferhringur"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Hringur"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Dropi"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Breytir formi tákns"</string>
-    <string name="package_state_unknown" msgid="7592128424511031410">"Óþekkt"</string>
-    <string name="abandoned_clean_this" msgid="7610119707847920412">"Fjarlægja"</string>
-    <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_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="action_add_to_workspace" msgid="8902165848117513641">"Bæta á heimaskjá"</string>
-    <string name="action_move_here" msgid="2170188780612570250">"Færa atriði hingað"</string>
-    <string name="item_added_to_workspace" msgid="4211073925752213539">"Atriði bætt á heimaskjáinn"</string>
-    <string name="item_removed" msgid="851119963877842327">"Atriði fjarlægt"</string>
-    <string name="action_move" msgid="4339390619886385032">"Færa atriði"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Færa í línu <xliff:g id="NUMBER_0">%1$s</xliff:g>, dálk <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
-    <string name="move_to_position" msgid="6750008980455459790">"Færa í stöðu <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
-    <string name="move_to_hotseat_position" msgid="6295412897075147808">"Færa í stöðu <xliff:g id="NUMBER">%1$s</xliff:g> á festisvæði"</string>
-    <string name="item_moved" msgid="4606538322571412879">"Atriði fært"</string>
-    <string name="add_to_folder" msgid="9040534766770853243">"Setja í möppu: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="add_to_folder_with_app" msgid="4534929978967147231">"Setja í möppu með <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="added_to_folder" msgid="4793259502305558003">"Atriði sett í möppu"</string>
-    <string name="create_folder_with" msgid="4050141361160214248">"Búa til möppu með: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="folder_created" msgid="6409794597405184510">"Mappa búin til"</string>
-    <string name="action_move_to_workspace" msgid="1603837886334246317">"Færa á heimaskjá"</string>
-    <string name="action_resize" msgid="1802976324781771067">"Breyta stærð"</string>
-    <string name="action_increase_width" msgid="8773715375078513326">"Auka breidd"</string>
-    <string name="action_increase_height" msgid="459390020612501122">"Auka hæð"</string>
-    <string name="action_decrease_width" msgid="1374549771083094654">"Minnka breidd"</string>
-    <string name="action_decrease_height" msgid="282377193880900022">"Minnka hæð"</string>
-    <string name="widget_resized" msgid="9130327887929620">"Stærð græju breytt í <xliff:g id="NUMBER_0">%1$s</xliff:g> á breidd og <xliff:g id="NUMBER_1">%2$s</xliff:g> á hæð"</string>
-    <string name="action_deep_shortcut" msgid="2864038805849372848">"Flýtileiðir"</string>
-    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> flýtileiðir fyrir <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
-    <string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> flýtileiðir og <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> tilkynningar fyrir <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
-    <string name="action_dismiss_notification" msgid="5909461085055959187">"Hunsa"</string>
-    <string name="notification_dismissed" msgid="6002233469409822874">"Tilkynningu lokað"</string>
-    <string name="all_apps_personal_tab" msgid="4190252696685155002">"Persónulegt"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"Vinna"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"Vinnusnið"</string>
-    <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Hér finnurðu vinnuforrit"</string>
-    <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Öll vinnuforrit eru með merki og fyrirtækið þitt tryggir öryggi þeirra. Færðu forrit yfir á heimaskjáinn til að fá auðveldari aðgang að þeim."</string>
-    <string name="work_mode_on_label" msgid="4781128097185272916">"Stjórnað af fyrirtækinu þínu"</string>
-    <string name="work_mode_off_label" msgid="3194894777601421047">"Slökkt er á tilkynningum og forritum"</string>
-    <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Loka"</string>
-    <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Lokað"</string>
-</resources>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 04842e4..d66ca32 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -48,7 +49,7 @@
     <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">"Fjarlægja"</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>
     <string name="install_drop_target_label" msgid="2539096853673231757">"Setja upp"</string>
@@ -64,7 +65,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Þetta er kerfisforrit sem ekki er hægt að fjarlægja."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Ónefnd mappa"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Óvirkt <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, er með <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> tilkynningu</item>
       <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, er með <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> tilkynningar</item>
     </plurals>
@@ -79,29 +80,22 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Mappa: <xliff:g id="NAME">%1$s</xliff:g>"</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_desc" msgid="8662546029078692509">"Þegar símanum er snúið"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Tilkynningapunktar"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Kveikt"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Slökkt"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Tilkynningapunktar"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Kveikt"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Slökkt"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Aðgangs að tilkynningum er krafist"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Til að sýna tilkynningarpunkta skaltu kveikja á forritstilkynningum fyrir <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Breyta stillingum"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Sýna tilkynningapunkta"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Sýna tilkynningapunkta"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Bæta tákni á heimaskjáinn"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Fyrir ný forrit"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Breyta formi tákns"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"á heimaskjá"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Nota sjálfgildi kerfis"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Ferningur"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Ferhringur"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Hringur"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Dropi"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Breytir formi tákns"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Óþekkt"</string>
-    <string name="abandoned_clean_this" msgid="7610119707847920412">"Fjarlægja"</string>
+    <string name="abandoned_clean_this" msgid="7610119707847920412">"Taka niður"</string>
     <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>
@@ -114,6 +108,7 @@
     <string name="action_move_here" msgid="2170188780612570250">"Færa atriði hingað"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Atriði bætt á heimaskjáinn"</string>
     <string name="item_removed" msgid="851119963877842327">"Atriði fjarlægt"</string>
+    <string name="undo" msgid="4151576204245173321">"Afturkalla"</string>
     <string name="action_move" msgid="4339390619886385032">"Færa atriði"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Færa í línu <xliff:g id="NUMBER_0">%1$s</xliff:g>, dálk <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Færa í stöðu <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Slökkt er á tilkynningum og forritum"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Loka"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Lokað"</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 28a8f93..8c4e3c5 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,9 +65,9 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Questa è un\'app di sistema e non può essere disinstallata."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Cartella senza nome"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"App <xliff:g id="APP_NAME">%1$s</xliff:g> disattivata"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ha <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifiche</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> ha <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notifica</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ha <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifiche</item>
+      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, ha <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notifica</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"Pagina %1$d di %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Schermata Home %1$d di %2$d"</string>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Cartella: <xliff:g id="NAME">%1$s</xliff:g>"</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="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>
-    <string name="icon_badging_title" msgid="874121399231955394">"Indicatori notifica"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Attiva"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Non attiva"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Indicatori di notifica"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"On"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Off"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Accesso alle notifiche necessario"</string>
     <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="icon_badging_service_title" msgid="2309733118428242174">"Mostra indicatori di notifica"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Mostra indicatori di notifica"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Aggiungi icone alla schermata Home"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Per le nuove app"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Cambia la forma delle icone"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"nella schermata Home"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Usa impostazione predefinita di sistema"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Quadrato"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Supercerchio"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Cerchio"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Goccia"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Applicazione delle modifiche alla forma delle icone"</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>
@@ -114,6 +108,7 @@
     <string name="action_move_here" msgid="2170188780612570250">"Sposta elemento qui"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Elemento aggiunto alla schermata Home"</string>
     <string name="item_removed" msgid="851119963877842327">"Elemento rimosso"</string>
+    <string name="undo" msgid="4151576204245173321">"Annulla"</string>
     <string name="action_move" msgid="4339390619886385032">"Sposta elemento"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Sposta a riga <xliff:g id="NUMBER_0">%1$s</xliff:g>, colonna <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Sposta nella posizione <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Le notifiche e le app non sono attive"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Chiudi"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Chiusa"</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 d4302c6..c25e879 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -33,13 +33,14 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"הקש פעמיים וגע נגיעה רציפה בווידג\'ט כדי לבחור בו, או השתמש בפעולות מותאמות אישית."</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="add_item_request_drag_hint" msgid="5899764264480397019">"יש ללחוץ לחיצה ארוכה כדי להוסיף ידנית"</string>
     <string name="place_automatically" msgid="8064208734425456485">"הוסף באופן אוטומטי"</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="notifications_header" msgid="1404149926117359025">"הודעות"</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>
@@ -49,7 +50,7 @@
     <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="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="permlab_install_shortcut" msgid="5632423390354674437">"התקן קיצורי דרך"</string>
@@ -60,15 +61,15 @@
     <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>
+    <string name="gadget_setup_text" msgid="8274003207686040488">"הגדרה"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"זוהי אפליקציית מערכת ולא ניתן להסיר את התקנתה."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"תיקיה ללא שם"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> מושבתת"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="two">לאפליקציה <xliff:g id="APP_NAME_2">%1$s</xliff:g> יש <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> הודעות</item>
-      <item quantity="many">לאפליקציה <xliff:g id="APP_NAME_2">%1$s</xliff:g> יש <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> הודעות</item>
-      <item quantity="other">לאפליקציה <xliff:g id="APP_NAME_2">%1$s</xliff:g> יש <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> הודעות</item>
-      <item quantity="one">לאפליקציה <xliff:g id="APP_NAME_0">%1$s</xliff:g> יש הודעה אחת (<xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g>)</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="two">לאפליקציה <xliff:g id="APP_NAME_2">%1$s</xliff:g> יש <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> התראות</item>
+      <item quantity="many">לאפליקציה <xliff:g id="APP_NAME_2">%1$s</xliff:g> יש <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> התראות</item>
+      <item quantity="other">לאפליקציה <xliff:g id="APP_NAME_2">%1$s</xliff:g> יש <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> התראות</item>
+      <item quantity="one">לאפליקציה <xliff:g id="APP_NAME_0">%1$s</xliff:g> יש התראה אחת (<xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g>)</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"‏דף %1$d מתוך %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"‏מסך דף הבית %1$d מתוך %2$d"</string>
@@ -81,27 +82,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"תיקיה: <xliff:g id="NAME">%1$s</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="icon_badging_title" msgid="874121399231955394">"סימני הודעות"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"מופעלת"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"כבויה"</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="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="icon_badging_service_title" msgid="2309733118428242174">"הצגה של סימן ההודעות"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"הצגת סימני ההתראות"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"הוספת סמל במסך דף הבית"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"לאפליקציות חדשות"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"שינוי הצורה של הסמלים"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"במסך דף הבית"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"השתמש בברירת המחדל של המערכת"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"ריבוע"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"ריבוע בעל פינות מעוגלות"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"מעגל"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"טיפה"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"משנה את הצורה של הסמלים"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"לא ידוע"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"הסר"</string>
     <string name="abandoned_search" msgid="891119232568284442">"חיפוש"</string>
@@ -116,6 +110,7 @@
     <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>
@@ -134,16 +129,17 @@
     <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>
+    <string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"קיצורי דרך והתראות"</string>
     <string name="action_dismiss_notification" msgid="5909461085055959187">"סגור"</string>
-    <string name="notification_dismissed" msgid="6002233469409822874">"ההודעה נסגרה"</string>
+    <string name="notification_dismissed" msgid="6002233469409822874">"ההתראה נסגרה"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"אישיות"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"עבודה"</string>
     <string name="work_profile_toggle_label" msgid="3081029915775481146">"פרופיל עבודה"</string>
     <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"ניתן למצוא כאן את אפליקציות העבודה"</string>
     <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"לכל אפליקציית עבודה יש תג ואבטחתה מטופלת בידי הארגון. אפשר להעביר אפליקציות אל מסך דף הבית כדי להקל את הגישה אליהן."</string>
     <string name="work_mode_on_label" msgid="4781128097185272916">"מנוהל בידי הארגון"</string>
-    <string name="work_mode_off_label" msgid="3194894777601421047">"הודעות ואפליקציות כבויות"</string>
+    <string name="work_mode_off_label" msgid="3194894777601421047">"התראות ואפליקציות כבויות"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"סגירה"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"סגור"</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 2598d18..354f020 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -33,12 +33,13 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"ダブルタップ後に押し続けてウィジェットを選択するか、カスタム操作を使用してください。"</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="add_item_request_drag_hint" msgid="5899764264480397019">"長押しすると、手動で追加できます"</string>
     <string name="place_automatically" msgid="8064208734425456485">"自動的に追加"</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>
@@ -64,9 +65,9 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"このシステムアプリはアンインストールできません。"</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"名前のないフォルダ"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」は無効です"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>: <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> 件の通知</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>: <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> 件の通知</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> の通知が <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> 件あります</item>
+      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> の通知が <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> 件あります</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"%1$d/%2$dページ"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"ホーム画面: %1$d/%2$d"</string>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"フォルダ: <xliff:g id="NAME">%1$s</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="icon_badging_title" msgid="874121399231955394">"通知ドット"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"ON"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"OFF"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"通知ドット"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"ON"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"OFF"</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>」のアプリ通知を ON にしてください"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"設定を変更"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"通知ドットの表示"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"通知ドットの表示"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ホーム画面にアイコンを追加"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"新しいアプリをダウンロードしたとき"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"アイコンの形の変更"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"ホーム画面上"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"システムのデフォルトを使用"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"スクエア"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"スクワークル"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"サークル"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"ティアドロップ"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"アイコンの形の変更を適用しています"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"不明"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"削除"</string>
     <string name="abandoned_search" msgid="891119232568284442">"検索"</string>
@@ -114,6 +108,7 @@
     <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>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"通知とアプリは OFF です"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"閉じる"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"終了"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"失敗: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml
deleted file mode 100644
index 521ec80..0000000
--- a/res/values-ka-rGE/strings.xml
+++ /dev/null
@@ -1,146 +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">
-    <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
-    <string name="folder_name" msgid="7371454440695724752"></string>
-    <string name="work_folder_name" msgid="3753320833950115786">"სამუშაო"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"აპი არ არის დაყენებული."</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"აპი მიუწვდომელია"</string>
-    <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="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="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="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="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="permlab_install_shortcut" msgid="5632423390354674437">"მალსახმობების დაყენება"</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="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>
-    <string name="gadget_setup_text" msgid="8274003207686040488">"დაყენება"</string>
-    <string name="uninstall_system_app_text" msgid="4172046090762920660">"ეს სისტემური აპია და მისი წაშლა შეუძლებელია."</string>
-    <string name="folder_hint_text" msgid="6617836969016293992">"უსახელო საქაღალდე"</string>
-    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> გაითიშა"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>-ში <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> შეტყობინებაა</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>-ში <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> შეტყობინებაა</item>
-    </plurals>
-    <string name="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> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="4625795376335528256">"შეეხეთ საქაღალდის დასახურად"</string>
-    <string name="folder_tap_to_rename" msgid="4017685068016979677">"შეეხეთ გადარქმეული სახელის შესანახად"</string>
-    <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" msgid="6629239338071103179">"საქაღალდე: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"ვიჯეტები"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"ფონები"</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="icon_badging_title" msgid="874121399231955394">"შეტყობინების ნიშნულები"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"ჩართული"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"გამორთული"</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="icon_badging_service_title" msgid="2309733118428242174">"შეტყობინების ნიშნულების ჩვენება"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ხატულას მთავარ ეკრანზე დამატება"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"ახალი აპებისთვის"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"ხატულას ფორმის შეცვლა"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"მთავარ ეკრანზე"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"ნაგულისხმევი სისტემური პარამეტრების გამოყენება"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"კვადრატი"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"წრეკუთხედი"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"წრე"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"წვეთი"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"მიმდინარეობს ხატულას ფორმის ცვლილებების მიყენება"</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_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="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="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="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>
-    <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_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g>-ს აქვს <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> მალსახმობი"</string>
-    <string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g>-ის <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> მალსახმობი და <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> შეტყობინება"</string>
-    <string name="action_dismiss_notification" msgid="5909461085055959187">"დახურვა"</string>
-    <string name="notification_dismissed" msgid="6002233469409822874">"შეტყობინება დაიხურა"</string>
-    <string name="all_apps_personal_tab" msgid="4190252696685155002">"პირადი"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"სამსახური"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"სამსახურის პროფილი"</string>
-    <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"აქ თავმოყრილია სამსახურის აპები"</string>
-    <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"სამსახურის თითოეულ აპს აქვს ბეჯი, რაც ნიშნავს, რომ მათ უსაფრთხოებას თქვენი ორგანიზაცია უზრუნველყოფს. მარტივი წვდომისთვის, შეგიძლიათ სამსახურის აპები მთავარი ეკრანზე გადაიტანოთ."</string>
-    <string name="work_mode_on_label" msgid="4781128097185272916">"იმართება თქვენი ორგანიზაციის მიერ"</string>
-    <string name="work_mode_off_label" msgid="3194894777601421047">"შეტყობინებები და აპები გამორთულია"</string>
-    <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"დახურვა"</string>
-    <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"დახურული"</string>
-</resources>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index 3bdbab7..e9afef9 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,9 +65,9 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"ეს სისტემური აპია და მისი წაშლა შეუძლებელია."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"უსახელო საქაღალდე"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> გაითიშა"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>-ში <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> შეტყობინებაა</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>-ში <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> შეტყობინებაა</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>-ში არის <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> შეტყობინება</item>
+      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>-ში არის <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> შეტყობინება</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"გვერდი %1$d %2$d-დან"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"მთავარი ეკრანი %1$d, %2$d-დან"</string>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"საქაღალდე: <xliff:g id="NAME">%1$s</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="icon_badging_title" msgid="874121399231955394">"შეტყობინების ნიშნულები"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"ჩართული"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"გამორთული"</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="icon_badging_service_title" msgid="2309733118428242174">"შეტყობინების ნიშნულების ჩვენება"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"შეტყობინების ნიშნულების ჩვენება"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ხატულას მთავარ ეკრანზე დამატება"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"ახალი აპებისთვის"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"ხატულას ფორმის შეცვლა"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"მთავარ ეკრანზე"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"ნაგულისხმევი სისტემური პარამეტრების გამოყენება"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"კვადრატი"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"წრეკუთხედი"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"წრე"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"წვეთი"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"მიმდინარეობს ხატულას ფორმის ცვლილებების მიყენება"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"უცნობი"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ამოშლა"</string>
     <string name="abandoned_search" msgid="891119232568284442">"ძიება"</string>
@@ -114,6 +108,7 @@
     <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>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"შეტყობინებები და აპები გამორთულია"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"დახურვა"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"დახურული"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"ვერ მოხერხდა: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-kk-rKZ/strings.xml b/res/values-kk-rKZ/strings.xml
deleted file mode 100644
index 7ab73d9..0000000
--- a/res/values-kk-rKZ/strings.xml
+++ /dev/null
@@ -1,146 +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">
-    <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
-    <string name="folder_name" msgid="7371454440695724752"></string>
-    <string name="work_folder_name" msgid="3753320833950115786">"Жұмыс"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"Қолданба орнатылмаған."</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"Қолданба қол жетімді емес"</string>
-    <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="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="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="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="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="permlab_install_shortcut" msgid="5632423390354674437">"төте пернелерді орнату"</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="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>
-    <string name="gadget_setup_text" msgid="8274003207686040488">"Орнату"</string>
-    <string name="uninstall_system_app_text" msgid="4172046090762920660">"Бұл жүйе қолданбасы, сондықтан оны алу мүмкін емес."</string>
-    <string name="folder_hint_text" msgid="6617836969016293992">"Атауы жоқ қалта"</string>
-    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> өшірілді"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> қолданбасында <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> хабарландыру бар</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> қолданбасында <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> хабарландыру бар</item>
-    </plurals>
-    <string name="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_name_format" msgid="6629239338071103179">"Қалта: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Виджеттер"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"Тұсқағаздар"</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="icon_badging_title" msgid="874121399231955394">"Хабарландыру белгілері"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Қосулы"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Өшірулі"</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="icon_badging_service_title" msgid="2309733118428242174">"Хабарландыру белгілерін көрсету"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Негізгі экранға белгіше енгізу"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Жаңа қолданбаларға арналған"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Белгіше пішінін өзгерту"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"Негізгі экранда"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Жүйенің әдепкі параметрін пайдалану"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Шаршы"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Жұмыр төртбұрыш"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Шеңбер"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Тамшы"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Белгіше пішіні өзгерістері күшіне енуде"</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_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="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="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="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>
-    <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_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> қолданбасына арналған <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> таңбаша"</string>
-    <string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> қолданбасының <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> таңбашасы мен <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> хабарландыруы"</string>
-    <string name="action_dismiss_notification" msgid="5909461085055959187">"Бас тарту"</string>
-    <string name="notification_dismissed" msgid="6002233469409822874">"Хабарландырудан бас тартылды"</string>
-    <string name="all_apps_personal_tab" msgid="4190252696685155002">"Жеке"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"Жұмыс"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"Жұмыс профилі"</string>
-    <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Жұмыс қолданбалары осы жерде берілген"</string>
-    <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Әрбір жұмыс қолданбасында танымбелгі бар. Ол оның қауіпсіздігі ұйым арқылы қамтамасыз етілетінін білдіреді. Жұмыс қолданбаларына оңай кіру үшін, оларды Негізгі экранға жылжытуға болады."</string>
-    <string name="work_mode_on_label" msgid="4781128097185272916">"Ұйым арқылы басқарылады"</string>
-    <string name="work_mode_off_label" msgid="3194894777601421047">"Хабарландырулар мен қолданбалар өшірулі"</string>
-    <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Жабу"</string>
-    <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Жабық"</string>
-</resources>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index 98afccc..1cd9045 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -34,11 +34,12 @@
     <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>
     <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>
@@ -48,7 +49,7 @@
     <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>
@@ -64,7 +65,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Бұл жүйе қолданбасы, сондықтан оны алу мүмкін емес."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Атауы жоқ қалта"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> өшірілді"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> қолданбасында <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> хабарландыру бар</item>
       <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> қолданбасында <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> хабарландыру бар</item>
     </plurals>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Қалта: <xliff:g id="NAME">%1$s</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="icon_badging_title" msgid="874121399231955394">"Хабарландыру белгілері"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Қосулы"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Өшірулі"</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="icon_badging_service_title" msgid="2309733118428242174">"Хабарландыру белгілерін көрсету"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Хабарландыру белгілерін көрсету"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Негізгі экранға белгіше енгізу"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Жаңа қолданбаларға арналған"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Белгіше пішінін өзгерту"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"Негізгі экранда"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Жүйенің әдепкі параметрін пайдалану"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Шаршы"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Жұмыр төртбұрыш"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Шеңбер"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Тамшы"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Белгіше пішіні өзгерістері күшіне енуде"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Белгісіз"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Алып тастау"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Іздеу"</string>
@@ -114,6 +108,7 @@
     <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>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Хабарландырулар мен қолданбалар өшірулі"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Жабу"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Жабық"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"Қате шықты: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml
deleted file mode 100644
index 582bb33..0000000
--- a/res/values-km-rKH/strings.xml
+++ /dev/null
@@ -1,146 +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">
-    <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
-    <string name="folder_name" msgid="7371454440695724752"></string>
-    <string name="work_folder_name" msgid="3753320833950115786">"ការងារ"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"មិន​បាន​ដំឡើង​កម្មវិធី។"</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"មិន​មាន​កម្មវិធី"</string>
-    <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="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="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="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="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="permlab_install_shortcut" msgid="5632423390354674437">"ដំឡើង​ផ្លូវកាត់"</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="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>
-    <string name="gadget_setup_text" msgid="8274003207686040488">"រៀបចំ"</string>
-    <string name="uninstall_system_app_text" msgid="4172046090762920660">"នេះ​​​ជា​កម្មវិធី​ប្រព័ន្ធ មិន​អាច​លុប​បាន​ទេ។"</string>
-    <string name="folder_hint_text" msgid="6617836969016293992">"ថត​គ្មាន​ឈ្មោះ"</string>
-    <string name="disabled_app_label" msgid="6673129024321402780">"បានបិទដំណើរការ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> មាន​ការ​ជូន​ដំណឹង <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g></item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> មាន​ការ​ជូន​ដំណឹង <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g></item>
-    </plurals>
-    <string name="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_name_format" msgid="6629239338071103179">"ថត៖ <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"ធាតុ​ក្រាហ្វិក"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"ផ្ទាំង​រូបភាព"</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="icon_badging_title" msgid="874121399231955394">"ស្លាកជូនដំណឹង"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"បើក"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"បិទ"</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="icon_badging_service_title" msgid="2309733118428242174">"បង្ហាញ​ស្លាក​ជូនដំណឹង"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"បញ្ចូល​រូបតំណាង​ទៅ​អេក្រង់​ដើម"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"សម្រាប់កម្មវិធីថ្មី"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"ប្តូររូបរាងរូបតំណាង"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"នៅ​លើ​អេក្រង់​ដើម"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"ប្រើលំនាំដើមរបស់ប្រព័ន្ធ"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"ការ៉េ"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"ការ៉េជ្រុងកោង"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"រង្វង់"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"តំណក់​ទឹកភ្នែក"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"កំពុងអនុវត្ត​ការប្តូររូបរាងរូបតំណាង"</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_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="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="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="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>
-    <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_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> ផ្លូវកាត់សម្រាប់ <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
-    <string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"ផ្លូវកាត់ចំនួន <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> និង​ការជូនដំណឹងចំនួន <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> សម្រាប់ <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
-    <string name="action_dismiss_notification" msgid="5909461085055959187">"បដិសេធ"</string>
-    <string name="notification_dismissed" msgid="6002233469409822874">"បាន​បដិសេធ​ការជូនដំណឹង"</string>
-    <string name="all_apps_personal_tab" msgid="4190252696685155002">"ផ្ទាល់ខ្លួន"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"ការងារ"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"កម្រងព័ត៌មានការងារ"</string>
-    <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"ស្វែង​រក​កម្មវិធី​ការងារ​នៅទីនេះ"</string>
-    <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"កម្មវិធី​ការងារ​នីមួយៗ​មាន​ស្លាកមួយ និងត្រូវ​បានរក្សាទុក​យ៉ាងមានសុវត្ថិភាព​ដោយស្ថាប័ន​របស់អ្នក។ សូម​ផ្លាស់ទី​កម្មវិធី​ទៅកាន់​អេក្រង់​ដើម​របស់អ្នក​ ដើម្បី​ងាយស្រួល​ចូលប្រើជាងមុន។"</string>
-    <string name="work_mode_on_label" msgid="4781128097185272916">"គ្រប់គ្រងដោយ​ស្ថាប័ន​របស់អ្នក"</string>
-    <string name="work_mode_off_label" msgid="3194894777601421047">"ការជូនដំណឹង​ និងកម្មវិធី​ត្រូវបានបិទ"</string>
-    <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"បិទ"</string>
-    <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"បានបិទ"</string>
-</resources>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index ba8d775..f4a328d 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,9 +65,9 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"នេះ​​​ជា​កម្មវិធី​ប្រព័ន្ធ មិន​អាច​លុប​បាន​ទេ។"</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"ថត​គ្មាន​ឈ្មោះ"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"បានបិទដំណើរការ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> មាន​ការ​ជូន​ដំណឹង <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g></item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> មាន​ការ​ជូន​ដំណឹង <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g></item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, មាន​ការជូនដំណឹង <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g></item>
+      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, មាន​ការជូនដំណឺង <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g></item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"ទំព័រ %1$d នៃ %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"អេក្រង់​ដើម %1$d នៃ %2$d"</string>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"ថត៖ <xliff:g id="NAME">%1$s</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="icon_badging_title" msgid="874121399231955394">"ស្លាកជូនដំណឹង"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"បើក"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"បិទ"</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="icon_badging_service_title" msgid="2309733118428242174">"បង្ហាញ​ស្លាក​ជូនដំណឹង"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"បង្ហាញ​ស្លាក​ជូនដំណឹង"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"បញ្ចូល​រូបតំណាង​ទៅ​អេក្រង់​ដើម"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"សម្រាប់កម្មវិធីថ្មី"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"ប្តូររូបរាងរូបតំណាង"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"នៅ​លើ​អេក្រង់​ដើម"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"ប្រើលំនាំដើមរបស់ប្រព័ន្ធ"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"ការ៉េ"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"ការ៉េជ្រុងកោង"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"រង្វង់"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"តំណក់​ទឹកភ្នែក"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"កំពុងអនុវត្ត​ការប្តូររូបរាងរូបតំណាង"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"មិន​ស្គាល់"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"លុបចេញ"</string>
     <string name="abandoned_search" msgid="891119232568284442">"ស្វែងរក"</string>
@@ -110,10 +104,11 @@
     <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="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>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"ការជូនដំណឹង​ និងកម្មវិធី​ត្រូវបានបិទ"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"បិទ"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"បានបិទ"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"បានបរាជ័យ៖ <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-kn-rIN/strings.xml b/res/values-kn-rIN/strings.xml
deleted file mode 100644
index 56ebe0a..0000000
--- a/res/values-kn-rIN/strings.xml
+++ /dev/null
@@ -1,146 +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">
-    <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
-    <string name="folder_name" msgid="7371454440695724752"></string>
-    <string name="work_folder_name" msgid="3753320833950115786">"ಕೆಲಸ"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"ಅಪ್ಲಿಕೇಶನ್‌ ಅನ್ನು ಸ್ಥಾಪಿಸಲಾಗಿಲ್ಲ"</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"ಅಪ್ಲಿಕೇಶನ್ ಲಭ್ಯವಿಲ್ಲ"</string>
-    <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="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="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="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="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="permlab_install_shortcut" msgid="5632423390354674437">"ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಸ್ಥಾಪಿಸಿ"</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="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>
-    <string name="gadget_setup_text" msgid="8274003207686040488">"ಸೆಟಪ್"</string>
-    <string name="uninstall_system_app_text" msgid="4172046090762920660">"ಇದೊಂದು ಅಪ್ಲಿಕೇಶನ್ ಆಗಿದೆ ಮತ್ತು ಅಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
-    <string name="folder_hint_text" msgid="6617836969016293992">"ಹೆಸರಿಲ್ಲದ ಫೋಲ್ಡರ್"</string>
-    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ಅಧಿಸೂಚನೆಗಳನ್ನು ಹೊಂದಿದೆ</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ಅಧಿಸೂಚನೆಗಳನ್ನು ಹೊಂದಿದೆ</item>
-    </plurals>
-    <string name="default_scroll_format" msgid="7475544710230993317">"%2$d ರಲ್ಲಿ %1$d ಪುಟ"</string>
-    <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d ರಲ್ಲಿ %1$d ಮುಖಪುಟದ ಪರದೆ"</string>
-    <string name="workspace_new_page" msgid="257366611030256142">"ಹೊಸ ಮುಖಪುಟ ಪರದೆ"</string>
-    <string name="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" msgid="6629239338071103179">"ಫೋಲ್ಡರ್: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"ವಿಜೆಟ್‌ಗಳು"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"ವಾಲ್‌ಪೇಪರ್‌ಗಳು"</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="icon_badging_title" msgid="874121399231955394">"ಅಧಿಸೂಚನೆ ಡಾಟ್‌ಗಳು"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"ಆನ್"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"ಆಫ್"</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="icon_badging_service_title" msgid="2309733118428242174">"ಅಧಿಸೂಚನೆ ಡಾಟ್‌ಗಳನ್ನು ತೋರಿಸಿ"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ಮುಖಪುಟದ ಪರದೆಗೆ ಐಕಾನ್ ಸೇರಿಸಿ"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"ಹೊಸ ಅಪ್ಲಿಕೇಶನ್‌ಗಳಿಗೆ"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"ಐಕಾನ್ ಆಕಾರವನ್ನು ಬದಲಿಸಿ"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"ಮುಖಪುಟ ಪರದೆಯಲ್ಲಿ"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"ಸಿಸ್ಟಂ ಡಿಫಾಲ್ಟ್ ಬಳಸಿ"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"ಚೌಕ"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"ಚೌಕವೃತ್ತ"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"ವೃತ್ತ"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"ಕಂಬನಿ"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"ಐಕಾನ್ ಆಕಾರ ಬದಲಾವಣೆಯನ್ನು ಅನ್ವಯಿಸಲಾಗುತ್ತಿದೆ"</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_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="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="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="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>
-    <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_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> ಗೆ <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> ಶಾರ್ಟ್‌ಕಟ್‌ಗಳು"</string>
-    <string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> ಗಾಗಿ <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> ಶಾರ್ಟ್‌ಕಟ್‌ಗಳು ಮತ್ತು <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> ಅಧಿಸೂಚನೆಗಳು"</string>
-    <string name="action_dismiss_notification" msgid="5909461085055959187">"ವಜಾಗೊಳಿಸಿ"</string>
-    <string name="notification_dismissed" msgid="6002233469409822874">"ಅಧಿಸೂಚನೆಯನ್ನು ವಜಾಗೊಳಿಸಲಾಗಿದೆ"</string>
-    <string name="all_apps_personal_tab" msgid="4190252696685155002">"ವೈಯಕ್ತಿಕ"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"ಕೆಲಸ"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"ಕೆಲಸದ ಪ್ರೊಫೈಲ್"</string>
-    <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"ಕೆಲಸದ ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಇಲ್ಲಿ ಹುಡುಕಿ"</string>
-    <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"ಕೆಲಸದ ಪ್ರತಿ ಅಪ್ಲಿಕೇಶನ್ ಬ್ಯಾಡ್ಜ್ ಹೊಂದಿದೆ ಮತ್ತು ನಿಮ್ಮ ಸಂಸ್ಥೆಯಿಂದ ಸುರಕ್ಷಿತವಾಗಿ ಇರಿಸಲಾಗುತ್ತದೆ. ಸುಲಭ ಪ್ರವೇಶಕ್ಕಾಗಿ ನಿಮ್ಮ ಹೋಮ್ ಸ್ಕ್ರೀನ್‌ಗೆ ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಸರಿಸಿ."</string>
-    <string name="work_mode_on_label" msgid="4781128097185272916">"ನಿಮ್ಮ ಸಂಸ್ಥೆಯ ಮೂಲಕ ನಿರ್ವಹಿಸಲಾಗಿದೆ"</string>
-    <string name="work_mode_off_label" msgid="3194894777601421047">"ಅಧಿಸೂಚನೆಗಳು ಮತ್ತು ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಆಫ್ ಆಗಿವೆ"</string>
-    <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"ಮುಚ್ಚಿ"</string>
-    <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"ಮುಚ್ಚಲಾಗಿದೆ"</string>
-</resources>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index e73ff39..14c8300 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,7 +65,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"ಇದೊಂದು ಅಪ್ಲಿಕೇಶನ್ ಆಗಿದೆ ಮತ್ತು ಅಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"ಹೆಸರಿಲ್ಲದ ಫೋಲ್ಡರ್"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ಅಧಿಸೂಚನೆಗಳನ್ನು ಹೊಂದಿದೆ</item>
       <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ಅಧಿಸೂಚನೆಗಳನ್ನು ಹೊಂದಿದೆ</item>
     </plurals>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"ಫೋಲ್ಡರ್: <xliff:g id="NAME">%1$s</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_desc" msgid="8662546029078692509">"ಫೋನ್‌ ತಿರುಗಿಸಿದಾಗ"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"ಅಧಿಸೂಚನೆ ಡಾಟ್‌ಗಳು"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"ಆನ್"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"ಆಫ್"</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="icon_badging_service_title" msgid="2309733118428242174">"ಅಧಿಸೂಚನೆ ಡಾಟ್‌ಗಳನ್ನು ತೋರಿಸಿ"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"ಅಧಿಸೂಚನೆ ಡಾಟ್‌ಗಳನ್ನು ತೋರಿಸಿ"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ಮುಖಪುಟದ ಪರದೆಗೆ ಐಕಾನ್ ಸೇರಿಸಿ"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"ಹೊಸ ಅಪ್ಲಿಕೇಶನ್‌ಗಳಿಗೆ"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"ಐಕಾನ್ ಆಕಾರವನ್ನು ಬದಲಿಸಿ"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"ಮುಖಪುಟ ಪರದೆಯಲ್ಲಿ"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"ಸಿಸ್ಟಂ ಡಿಫಾಲ್ಟ್ ಬಳಸಿ"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"ಚೌಕ"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"ಚೌಕವೃತ್ತ"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"ವೃತ್ತ"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"ಕಂಬನಿ"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"ಐಕಾನ್ ಆಕಾರ ಬದಲಾವಣೆಯನ್ನು ಅನ್ವಯಿಸಲಾಗುತ್ತಿದೆ"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"ಅಪರಿಚಿತ"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ತೆಗೆದುಹಾಕಿ"</string>
     <string name="abandoned_search" msgid="891119232568284442">"ಹುಡುಕಿ"</string>
@@ -108,14 +102,13 @@
     <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>
-    <!-- no translation found for widgets_list (796804551140113767) -->
-    <skip />
-    <!-- no translation found for widgets_list_closed (6141506579418771922) -->
-    <skip />
-    <string name="action_add_to_workspace" msgid="8902165848117513641">"ಮುಖಪುಟಕ್ಕೆ ಸೇರಿಸು"</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="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>
@@ -146,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"ಅಧಿಸೂಚನೆಗಳು ಮತ್ತು ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಆಫ್ ಆಗಿವೆ"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"ಮುಚ್ಚಿ"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"ಮುಚ್ಚಲಾಗಿದೆ"</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 5e68b17..58b65da 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -33,12 +33,13 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"위젯을 선택하려면 두 번 탭한 다음 길게 터치하거나 맞춤 액션을 사용합니다."</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="add_item_request_drag_hint" msgid="5899764264480397019">"길게 터치하여 직접 추가"</string>
     <string name="place_automatically" msgid="8064208734425456485">"자동으로 추가"</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>
@@ -64,9 +65,9 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"시스템 앱은 제거할 수 없습니다."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"이름이 없는 폴더"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> 사용 안함"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>개의 <xliff:g id="APP_NAME_2">%1$s</xliff:g> 알림 있음</item>
-      <item quantity="one"><xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g>개의 <xliff:g id="APP_NAME_0">%1$s</xliff:g> 알림 있음</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>에 <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>개의 알림이 있음</item>
+      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>에 <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g>개의 알림이 있음</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"페이지 %1$d/%2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"홈 화면 %1$d/%2$d"</string>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"폴더: <xliff:g id="NAME">%1$s</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="icon_badging_title" msgid="874121399231955394">"알림 표시 점"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"사용"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"사용 안함"</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="icon_badging_service_title" msgid="2309733118428242174">"알림 표시 점 보기"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"알림 표시 점 보기"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"홈 화면에 아이콘 추가"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"새로 설치한 앱에 적용"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"아이콘 모양 변경"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"홈 화면에 표시"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"시스템 기본값 사용"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"정사각형"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"모서리가 둥근 정사각형"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"원"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"눈물방울"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"아이콘 모양 변경사항을 적용하는 중입니다."</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"알 수 없음"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"삭제"</string>
     <string name="abandoned_search" msgid="891119232568284442">"검색"</string>
@@ -114,6 +108,7 @@
     <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>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"알림 및 앱 사용 중지됨"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"닫기"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"종료됨"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"실패: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-ky-rKG/strings.xml b/res/values-ky-rKG/strings.xml
deleted file mode 100644
index 502a08a..0000000
--- a/res/values-ky-rKG/strings.xml
+++ /dev/null
@@ -1,146 +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">
-    <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
-    <string name="folder_name" msgid="7371454440695724752"></string>
-    <string name="work_folder_name" msgid="3753320833950115786">"Жумуш"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"Колдонмо орнотулган эмес."</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"Колдонмо жеткиликтүү эмес"</string>
-    <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="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="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="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="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="permlab_install_shortcut" msgid="5632423390354674437">"тез чакырмаларды орнотуу"</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="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>
-    <string name="gadget_setup_text" msgid="8274003207686040488">"Орнотуу"</string>
-    <string name="uninstall_system_app_text" msgid="4172046090762920660">"Бул системдик колдонмо жана аны чечкенге болбойт."</string>
-    <string name="folder_hint_text" msgid="6617836969016293992">"Аты жок фолдер"</string>
-    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> өчүрүлгөн"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> эскертме бар</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> эскертме бар</item>
-    </plurals>
-    <string name="default_scroll_format" msgid="7475544710230993317">"%2$d ичинен %1$d барак"</string>
-    <string name="workspace_scroll_format" msgid="8458889198184077399">"Үй экраны %2$d ичинен %1$d"</string>
-    <string name="workspace_new_page" msgid="257366611030256142">"Жаңы башкы экран барагы"</string>
-    <string name="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" msgid="6629239338071103179">"Фолдер: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Виджеттер"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"Тушкагаздар"</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="icon_badging_title" msgid="874121399231955394">"Билдирмелер белгилери"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Күйүк"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Өчүк"</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="icon_badging_service_title" msgid="2309733118428242174">"Эскертме белгилерин көрсөтүү"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Башкы экранга сүрөтчө кошуу"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Жаңы колдонмолор үчүн"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Сүрөтчөнүн формасын өзгөртүү"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"Башкы экранда"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Демейки тутум жөндөөлөрү колдонулат"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Чарчы"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Бурчтары жумуру төрт бурчтук"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Тегерек"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Тамчы"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Өзгөртүлгөн сүрөтчөнүн формасы колдонулууда"</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_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="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="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="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>
-    <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_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> колдонмосуна <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> кыска жол бар"</string>
-    <string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> колдонмосу үчүн <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> кыска жол жана <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> эскертме бар"</string>
-    <string name="action_dismiss_notification" msgid="5909461085055959187">"Этибарга албоо"</string>
-    <string name="notification_dismissed" msgid="6002233469409822874">"Эскертме көз жаздымда калтырылды"</string>
-    <string name="all_apps_personal_tab" msgid="4190252696685155002">"Жеке колдонмолор"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"Жумуш колдонмолору"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"Жумуш профили"</string>
-    <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Жумуш колдонмолорун бул жерден таап алыңыз"</string>
-    <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Ар бир жумуш колдонмосунун бейджиги бар жана ал уюмуңуз тарабынан коопсуз сакталат. Колдонмолорго тез өтүү үчүн аларды Башкы экранга кошуп алыңыз."</string>
-    <string name="work_mode_on_label" msgid="4781128097185272916">"Уюмуңуз тарабынан башкарылат"</string>
-    <string name="work_mode_off_label" msgid="3194894777601421047">"Билдирүүлөр жана колдонмолор өчүрүлгөн"</string>
-    <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Жабуу"</string>
-    <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Жабык"</string>
-</resources>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index da7813e..046a662 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -33,12 +33,13 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Виджет тандоо үчүн эки жолу таптап, кармап туруңуз же ыңгайлаштырылган аракеттерди колдонуңуз."</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="add_item_request_drag_hint" msgid="5899764264480397019">"Кол менен кошуу үчүн кое бербей басып туруңуз"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Автоматтык түрдө кошуу"</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>
@@ -64,9 +65,9 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Бул системдик колдонмо жана аны чечкенге болбойт."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Аты жок фолдер"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> өчүрүлгөн"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> эскертме бар</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> эскертме бар</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> билдирмеси бар</item>
+      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> билдирмеси бар</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"%2$d ичинен %1$d барак"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Үй экраны %2$d ичинен %1$d"</string>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Фолдер: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Виджеттер"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Тушкагаздар"</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>
-    <string name="icon_badging_title" msgid="874121399231955394">"Билдирмелер белгилери"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Күйүк"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Өчүк"</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="icon_badging_service_title" msgid="2309733118428242174">"Эскертме белгилерин көрсөтүү"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Билдирмелер белгилерин көрсөтүү"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Башкы экранга сүрөтчө кошуу"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Жаңы колдонмолор үчүн"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Сүрөтчөнүн формасын өзгөртүү"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"Башкы экранда"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Демейки тутум жөндөөлөрү колдонулат"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Чарчы"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Бурчтары жумуру төрт бурчтук"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Тегерек"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Тамчы"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Өзгөртүлгөн сүрөтчөнүн формасы колдонулууда"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Белгисиз"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Алып салуу"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Издөө"</string>
@@ -114,6 +108,7 @@
     <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>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Билдирүүлөр жана колдонмолор өчүрүлгөн"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Жабуу"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Жабык"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"Аткарылган жок: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-land/dimens.xml b/res/values-land/dimens.xml
index f002195..bc658e4 100644
--- a/res/values-land/dimens.xml
+++ b/res/values-land/dimens.xml
@@ -27,11 +27,6 @@
     <!-- Dynamic grid -->
     <dimen name="dynamic_grid_icon_drawable_padding">4dp</dimen>
 
-    <dimen name="dynamic_grid_cell_layout_padding">0dp</dimen>
-    <dimen name="dynamic_grid_cell_layout_bottom_padding">5.5dp</dimen>
-
     <!-- Hotseat -->
-    <!-- Will be set to equal the hotseat icon size. -->
-    <dimen name="dynamic_grid_hotseat_size">0dp</dimen>
     <dimen name="dynamic_grid_hotseat_side_padding">16dp</dimen>
 </resources>
diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml
deleted file mode 100644
index cd2c933..0000000
--- a/res/values-lo-rLA/strings.xml
+++ /dev/null
@@ -1,146 +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">
-    <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
-    <string name="folder_name" msgid="7371454440695724752"></string>
-    <string name="work_folder_name" msgid="3753320833950115786">"ວຽກ"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"ແອັບຯບໍ່ໄດ້ຖືກຕິດຕັ້ງ."</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"ແອັບຯ​ໃຊ້​ບໍ່​ໄດ້"</string>
-    <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="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="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="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="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="permlab_install_shortcut" msgid="5632423390354674437">"ຕິດຕັ້ງທາງລັດ"</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="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>
-    <string name="gadget_setup_text" msgid="8274003207686040488">"ຕິດຕັ້ງ"</string>
-    <string name="uninstall_system_app_text" msgid="4172046090762920660">"ນີ້ແມ່ນແອັບຯຂອງລະບົບ ແລະບໍ່ສາມາດຖອນການຕິດຕັ້ງອອກໄດ້."</string>
-    <string name="folder_hint_text" msgid="6617836969016293992">"ໂຟນເດີຍັງບໍ່ຖືກຕັ້ງຊື່"</string>
-    <string name="disabled_app_label" msgid="6673129024321402780">"ປິດການນຳໃຊ້ <xliff:g id="APP_NAME">%1$s</xliff:g> ແລ້ວ"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ມີ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ການແຈ້ງເຕືອນ</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, ມີ <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> ການແຈ້ງເຕືອນ</item>
-    </plurals>
-    <string name="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_name_format" msgid="6629239338071103179">"ໂຟນເດີ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"ວິດເຈັດ"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"ພາບພື້ນຫຼັງ"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"ການຕັ້ງຄ່າ Home"</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="icon_badging_title" msgid="874121399231955394">"ຈຸດການແຈ້ງເຕືອນ"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"ເປີດ"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"ປິດ"</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="icon_badging_service_title" msgid="2309733118428242174">"ສະແດງຈຸດການແຈ້ງເຕືອນ"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ເພີ່ມໄອຄອນໃສ່ໜ້າຈໍຫຼັກ"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"ສຳລັບແອັບໃໝ່"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"ປ່ຽນຮູບຮ່າງໄອຄອນ"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"ຢູ່ໜ້າຈໍຫຼັກ"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"ໃຊ້ຄ່າເລີ່ມຕົ້ນລະບົບ"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"ສີ່ຫຼ່ຽມຈັດຕຸລັດ"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"ສີ່ຫຼ່ຽມຂອບມົນ"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"ວົງມົນ"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"ນ້ຳຢອດ"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"ນຳໃຊ້ການປ່ຽນແປງຮູບຮ່າງໄອຄອນ"</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_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="action_add_to_workspace" msgid="8902165848117513641">"ເພີ່ມໃສ່ໜ້າຈໍຫຼັກ"</string>
-    <string name="action_move_here" msgid="2170188780612570250">"Move item here"</string>
-    <string name="item_added_to_workspace" msgid="4211073925752213539">"ເພີ່ມ​ລາຍ​ການ​ໃສ່​ໜ້າ​ຈໍ​ຫຼັກ​ແລ້ວ"</string>
-    <string name="item_removed" msgid="851119963877842327">"ເອົາ​ລາຍ​ການ​ອອກ​ໄປ​ແລ້ວ"</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="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>
-    <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_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> ທາງລັດສຳລັບ <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
-    <string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> ທາງລັດ ແລະ <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> ການແຈ້ງເຕືອນສຳລັບ <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
-    <string name="action_dismiss_notification" msgid="5909461085055959187">"ປິດໄວ້"</string>
-    <string name="notification_dismissed" msgid="6002233469409822874">"ປິດການແຈ້ງເຕືອນແລ້ວ"</string>
-    <string name="all_apps_personal_tab" msgid="4190252696685155002">"ສ່ວນຕົວ"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"ວຽກ"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກ"</string>
-    <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"ຊອກຫາແອັບວຽກຢູ່ບ່ອນນີ້"</string>
-    <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"ແຕ່ລະແອັບວຽກຈະມີປ້າຍ ແລະ ຖືກຈັດເກັບໄວ້ຢ່າງປອດໄພໂດຍອົງກອນຂອງທ່ານ. ທ່ານສາມາດຍ້າຍແອັບໄປໃສ່ໜ້າຈໍຫຼັກເພື່ອໃຫ້ເຂົ້າໃຊ້ງ່າຍຂຶ້ນໄດ້."</string>
-    <string name="work_mode_on_label" msgid="4781128097185272916">"ຈັດການໂດຍອົງກອນຂອງທ່ານ"</string>
-    <string name="work_mode_off_label" msgid="3194894777601421047">"ການແຈ້ງເຕືອນ ແລະ ແອັບຖືກປິດໄວ້"</string>
-    <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"ປິດ"</string>
-    <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"ປິດແລ້ວ"</string>
-</resources>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index d0d127f..afe7664 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,7 +65,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"ນີ້ແມ່ນແອັບຯຂອງລະບົບ ແລະບໍ່ສາມາດຖອນການຕິດຕັ້ງອອກໄດ້."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"ໂຟນເດີຍັງບໍ່ຖືກຕັ້ງຊື່"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"ປິດການນຳໃຊ້ <xliff:g id="APP_NAME">%1$s</xliff:g> ແລ້ວ"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ມີ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ການແຈ້ງເຕືອນ</item>
       <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, ມີ <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> ການແຈ້ງເຕືອນ</item>
     </plurals>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"ໂຟນເດີ: <xliff:g id="NAME">%1$s</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="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="icon_badging_title" msgid="874121399231955394">"ຈຸດການແຈ້ງເຕືອນ"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"ເປີດ"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"ປິດ"</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="icon_badging_service_title" msgid="2309733118428242174">"ສະແດງຈຸດການແຈ້ງເຕືອນ"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"ສະແດງຈຸດການແຈ້ງເຕືອນ"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ເພີ່ມໄອຄອນໃສ່ໜ້າຈໍຫຼັກ"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"ສຳລັບແອັບໃໝ່"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"ປ່ຽນຮູບຮ່າງໄອຄອນ"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"ຢູ່ໜ້າຈໍຫຼັກ"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"ໃຊ້ຄ່າເລີ່ມຕົ້ນລະບົບ"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"ສີ່ຫຼ່ຽມຈັດຕຸລັດ"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"ສີ່ຫຼ່ຽມຂອບມົນ"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"ວົງມົນ"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"ນ້ຳຢອດ"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"ນຳໃຊ້ການປ່ຽນແປງຮູບຮ່າງໄອຄອນ"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"​ບໍ່​ຮູ້​ຈັກ"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ລຶບ​"</string>
     <string name="abandoned_search" msgid="891119232568284442">"ຊອກຫາ"</string>
@@ -114,6 +108,7 @@
     <string name="action_move_here" msgid="2170188780612570250">"Move item here"</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>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"ການແຈ້ງເຕືອນ ແລະ ແອັບຖືກປິດໄວ້"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"ປິດ"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"ປິດແລ້ວ"</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 c323fd8..6571582 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,11 +65,11 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Tai sistemos programa ir jos negalima pašalinti."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Aplankas be pavadinimo"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ išjungta"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="one">„<xliff:g id="APP_NAME_2">%1$s</xliff:g>“, yra <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> pranešimas</item>
-      <item quantity="few">„<xliff:g id="APP_NAME_2">%1$s</xliff:g>“, yra <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> pranešimai</item>
-      <item quantity="many">„<xliff:g id="APP_NAME_2">%1$s</xliff:g>“, yra <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> pranešimo</item>
-      <item quantity="other">„<xliff:g id="APP_NAME_2">%1$s</xliff:g>“, yra <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> pranešimų</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="one">Programoje „<xliff:g id="APP_NAME_2">%1$s</xliff:g>“ yra <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> pranešimas</item>
+      <item quantity="few">Programoje „<xliff:g id="APP_NAME_2">%1$s</xliff:g>“ yra <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> pranešimai</item>
+      <item quantity="many">Programoje „<xliff:g id="APP_NAME_2">%1$s</xliff:g>“ yra <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> pranešimo</item>
+      <item quantity="other">Programoje „<xliff:g id="APP_NAME_2">%1$s</xliff:g>“ yra <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> pranešimų</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"%1$d psl. iš %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%1$d pagrindinis ekranas iš %2$d"</string>
@@ -81,29 +82,22 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Aplankas: „<xliff:g id="NAME">%1$s</xliff:g>“"</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>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Išjungė administratorius"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Leisti pasukti pagrindinį ekraną"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Kai telefonas pasukamas"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Pranešimų taškai"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Įjungta"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Išjungta"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Pranešimų taškai"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Įjungta"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Išjungta"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Reikalinga prieiga prie pranešimų"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Kad būtų rodomi pranešimų taškai, įjunkite programos „<xliff:g id="NAME">%1$s</xliff:g>“ pranešimus."</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Keisti nustatymus"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Rodyti pranešimų taškus"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Rodyti pranešimų taškus"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Pridėti piktogr. prie pagrindinio ekrano"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Skirta naujoms programoms"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Pakeisti piktogramos formą"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"pagrindiniame ekrane"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Naudoti numatytuosius sistemos nustatymus"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Kvadratas"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Kvadratais suapvalintais kampais"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Apskritimas"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Ašara"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Taikomi piktogramos formos pakeitimai"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Nežinoma"</string>
-    <string name="abandoned_clean_this" msgid="7610119707847920412">"Pašalinti"</string>
+    <string name="abandoned_clean_this" msgid="7610119707847920412">"Panaikinti"</string>
     <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>
@@ -116,6 +110,7 @@
     <string name="action_move_here" msgid="2170188780612570250">"Perkelti elementą čia"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Elementas pridėtas prie pagrindinio ekrano"</string>
     <string name="item_removed" msgid="851119963877842327">"Elementas perkeltas"</string>
+    <string name="undo" msgid="4151576204245173321">"Anuliuoti"</string>
     <string name="action_move" msgid="4339390619886385032">"Perkelti elementą"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Perkelti į <xliff:g id="NUMBER_0">%1$s</xliff:g> eilutę, <xliff:g id="NUMBER_1">%2$s</xliff:g> stulpelį"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Perkelti į <xliff:g id="NUMBER">%1$s</xliff:g> poziciją"</string>
@@ -146,4 +141,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Programos ir pranešimai išjungti"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Uždaryti"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Uždaryta"</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 be03811..3da0fbb 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,10 +65,10 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Šī ir sistēmas lietotne, un to nevar atinstalēt."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Mape bez nosaukuma"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> ir atspējota"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="zero"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ir <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> paziņojumi</item>
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ir <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> paziņojums</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ir <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> paziņojumi</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="zero">Lietotnē <xliff:g id="APP_NAME_2">%1$s</xliff:g> ir <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> paziņojumi</item>
+      <item quantity="one">Lietotnē <xliff:g id="APP_NAME_2">%1$s</xliff:g> ir <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> paziņojums</item>
+      <item quantity="other">Lietotnē <xliff:g id="APP_NAME_2">%1$s</xliff:g> ir <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> paziņojumi</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"%1$d. lapa no %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Sākuma ekrāns: %1$d no %2$d"</string>
@@ -80,27 +81,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Mape: <xliff:g id="NAME">%1$s</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>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Atspējojis administrators"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Atļaut sākuma ekrāna pagriešanu"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Pagriežot tālruni"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Paziņojumu punkti"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Ieslēgts"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Izslēgts"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Paziņojumu punkti"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Ieslēgts"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Izslēgts"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Nepieciešama piekļuve paziņojumiem"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Lai tiktu rādīti paziņojumu punkti, ieslēdziet paziņojumus lietotnei <xliff:g id="NAME">%1$s</xliff:g>."</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Mainīt iestatījumus"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Rādīt paziņojumu punktus"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Rādīt paziņojumu punktus"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Pievienot ikonu sākuma ekrānā"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Jaunām lietotnēm"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Mainīt ikonu formu"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"sākuma ekrānā"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Izmantot sistēmas noklusējumu"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Kvadrāts"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Kvadrāts ar noapaļotiem stūriem"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Aplis"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Lāse"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Tiek piemērotas ikonu formas izmaiņas"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Nezināma"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Noņemt"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Meklēt"</string>
@@ -115,6 +109,7 @@
     <string name="action_move_here" msgid="2170188780612570250">"Pārvietot vienumu šeit"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Vienums pievienots sākuma ekrānam"</string>
     <string name="item_removed" msgid="851119963877842327">"Vienums noņemts"</string>
+    <string name="undo" msgid="4151576204245173321">"Atsaukt"</string>
     <string name="action_move" msgid="4339390619886385032">"Pārvietot vienumu"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Pārvietot uz <xliff:g id="NUMBER_0">%1$s</xliff:g>. rindu, <xliff:g id="NUMBER_1">%2$s</xliff:g>. kolonnu"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Pārvietot uz <xliff:g id="NUMBER">%1$s</xliff:g>. pozīciju"</string>
@@ -145,4 +140,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Paziņojumi un lietotnes ir izslēgtas"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Aizvērt"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Aizvērta"</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-rMK/strings.xml b/res/values-mk-rMK/strings.xml
deleted file mode 100644
index 54d9c80..0000000
--- a/res/values-mk-rMK/strings.xml
+++ /dev/null
@@ -1,146 +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">
-    <string name="app_name" msgid="649227358658669779">"Стартер3"</string>
-    <string name="folder_name" msgid="7371454440695724752"></string>
-    <string name="work_folder_name" msgid="3753320833950115786">"Работа"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"Апликацијата не е инсталирана."</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"Апликацијата не е достапна"</string>
-    <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="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="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="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="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="permlab_install_shortcut" msgid="5632423390354674437">"инсталирај кратенки"</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="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>
-    <string name="gadget_setup_text" msgid="8274003207686040488">"Поставување"</string>
-    <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ова е системска апликација и не може да се деинсталира."</string>
-    <string name="folder_hint_text" msgid="6617836969016293992">"Неименувана папка"</string>
-    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> е оневозможена"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> има <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> известување</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> има <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> известувања</item>
-    </plurals>
-    <string name="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_name_format" msgid="6629239338071103179">"Папка: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Виџети"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"Тапети"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Поставки за Home"</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="icon_badging_title" msgid="874121399231955394">"Точки за известување"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Вклучено"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Исклучено"</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="icon_badging_service_title" msgid="2309733118428242174">"Прикажи точки за известување"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Додај икона на почетниот екран"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"За нови апликации"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Променете ја формата на иконата"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"на „Почетен екран“"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Користи ја стандардната поставка на системот"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Квадрат"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Заоблен квадрат"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Круг"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Солза"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Се применуваат промените на формата на иконата"</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_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="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="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="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>
-    <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_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> кратенки за <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
-    <string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> кратенки и <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> известувања за <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
-    <string name="action_dismiss_notification" msgid="5909461085055959187">"Отфрли"</string>
-    <string name="notification_dismissed" msgid="6002233469409822874">"Известувањето е отфрлено"</string>
-    <string name="all_apps_personal_tab" msgid="4190252696685155002">"Лично"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"За работа"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"Работен профил"</string>
-    <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Најдете апликации за работа тука"</string>
-    <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Секоја апликација за работа има значка, а организацијата се грижи за нејзината безбедност. За полесен пристап, преместете ги апликациите на почетниот екран."</string>
-    <string name="work_mode_on_label" msgid="4781128097185272916">"Управувано од вашата организација"</string>
-    <string name="work_mode_off_label" msgid="3194894777601421047">"Известувањата и апликациите се исклучени"</string>
-    <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Затвори"</string>
-    <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Затворено"</string>
-</resources>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index e5dd027..9550a5f 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,7 +65,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ова е системска апликација и не може да се деинсталира."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Неименувана папка"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> е оневозможена"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> има <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> известување</item>
       <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> има <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> известувања</item>
     </plurals>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Папка: <xliff:g id="NAME">%1$s</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="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="icon_badging_title" msgid="874121399231955394">"Точки за известување"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Вклучено"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Исклучено"</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="icon_badging_service_title" msgid="2309733118428242174">"Прикажи точки за известување"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Прикажувај точки за известување"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Додај икона на почетниот екран"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"За нови апликации"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Променете ја формата на иконата"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"на „Почетен екран“"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Користи ја стандардната поставка на системот"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Квадрат"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Заоблен квадрат"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Круг"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Солза"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Се применуваат промените на формата на иконата"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Непознато"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Отстрани"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Барај"</string>
@@ -114,6 +108,7 @@
     <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>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Известувањата и апликациите се исклучени"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Затвори"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Затворено"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"Не успеа: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-ml-rIN/strings.xml b/res/values-ml-rIN/strings.xml
deleted file mode 100644
index a060933..0000000
--- a/res/values-ml-rIN/strings.xml
+++ /dev/null
@@ -1,146 +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">
-    <string name="app_name" msgid="649227358658669779">"ലോഞ്ചർ3"</string>
-    <string name="folder_name" msgid="7371454440695724752"></string>
-    <string name="work_folder_name" msgid="3753320833950115786">"ഔദ്യോഗികം"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"അപ്ലിക്കേഷൻ ഇൻസ്‌റ്റാളുചെ‌യ്‌തിട്ടില്ല."</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"അപ്ലിക്കേഷൻ ലഭ്യമല്ല"</string>
-    <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="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="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="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="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="permlab_install_shortcut" msgid="5632423390354674437">"കുറുക്കുവഴികൾ ഇൻസ്റ്റാളുചെയ്യുക"</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="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>
-    <string name="gadget_setup_text" msgid="8274003207686040488">"സജ്ജീകരിക്കുക"</string>
-    <string name="uninstall_system_app_text" msgid="4172046090762920660">"ഇതൊരു സിസ്‌റ്റം അപ്ലിക്കേഷനായതിനാൽ അൺഇൻസ്‌റ്റാളുചെയ്യാനാവില്ല."</string>
-    <string name="folder_hint_text" msgid="6617836969016293992">"പേരുനൽകാത്ത ഫോൾഡർ"</string>
-    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> പ്രവർത്തനരഹിതമാക്കി"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>-ന്, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> അറിയിപ്പുകൾ ഉണ്ട്</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>-ന്, <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> അറിയിപ്പ് ഉണ്ട്</item>
-    </plurals>
-    <string name="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_name_format" msgid="6629239338071103179">"ഫോൾഡർ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"വിജറ്റുകൾ"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"വാൾപേപ്പർ"</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="icon_badging_title" msgid="874121399231955394">"അറിയിപ്പ് ഡോട്ടുകൾ"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"ഓൺ"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"ഓഫ്"</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="icon_badging_service_title" msgid="2309733118428242174">"അറിയിപ്പ് ഡോട്ടുകൾ കാണിക്കുക"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ഹോം സ്ക്രീനിലേക്ക് ഐക്കൺ ചേർക്കുക"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"പുതിയ ആപ്പുകൾക്ക്"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"ഐക്കണിന്റെ ആകാരം മാറ്റുക"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"ഹോം സ്‌ക്രീനിൽ"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"സിസ്‌റ്റം ഡിഫോൾട്ട് ഉപയോഗിക്കുക"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"ചതുരം"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"ചതുരവൃത്തം"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"വൃത്തം"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"കണ്ണുനീര്‍ തുള്ളി"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"ഐക്കൺ ആകാര മാറ്റങ്ങൾ പ്രയോഗിക്കുന്നു"</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_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="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="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="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>
-    <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_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> ആപ്പിനുള്ള <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> കുറുക്കുവഴികൾ"</string>
-    <string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> ആപ്പിനായുള്ള <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> കുറുക്കുവഴികളും <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> അറിയിപ്പുകളും"</string>
-    <string name="action_dismiss_notification" msgid="5909461085055959187">"നിരസിക്കുക"</string>
-    <string name="notification_dismissed" msgid="6002233469409822874">"അറിയിപ്പ് നിരസിച്ചു"</string>
-    <string name="all_apps_personal_tab" msgid="4190252696685155002">"വ്യക്തിപരം"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"ജോലി"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"ഔദ്യോഗിക പ്രൊഫൈൽ"</string>
-    <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"ഔദ്യോഗിക ആപ്പുകൾ ഇവിടെ കണ്ടെത്തുക"</string>
-    <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"എല്ലാ ഔദ്യോഗിക ആപ്പിനും ഒരു ബാഡ്‌ജ് ഉണ്ട്, നിങ്ങളുടെ സ്ഥാപനം അത് സുരക്ഷിതമായി സൂക്ഷിക്കുന്നു. എളുപ്പത്തിൽ ആക്സസ് ചെയ്യാൻ ആപ്പുകളെ ഹോം സ്‌ക്രീനിലേക്ക് നീക്കുക."</string>
-    <string name="work_mode_on_label" msgid="4781128097185272916">"നിങ്ങളുടെ സ്ഥാപനം നിയന്ത്രിക്കുന്നത്"</string>
-    <string name="work_mode_off_label" msgid="3194894777601421047">"അറിയിപ്പുകളും ആപ്പുകളും ഓഫാണ്"</string>
-    <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"അടയ്ക്കുക"</string>
-    <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"അടച്ചു"</string>
-</resources>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index d6a2fd1..4362e7c 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -33,12 +33,13 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"വിജറ്റ് തിരഞ്ഞെടുക്കാനോ ഇഷ്ടാനുസൃത പ്രവർത്തനങ്ങൾ ഉപയോഗിക്കാനോ രണ്ടുതവണ ടാപ്പുചെയ്ത് പിടിക്കുക."</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="add_item_request_drag_hint" msgid="5899764264480397019">"നേരിട്ട് സ്ഥാപിക്കുന്നതിന് സ്‌പർശിച്ചുപിടിക്കുക"</string>
     <string name="place_automatically" msgid="8064208734425456485">"സ്വയമേവ ചേർക്കുക"</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>
@@ -64,9 +65,9 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"ഇതൊരു സിസ്‌റ്റം അപ്ലിക്കേഷനായതിനാൽ അൺഇൻസ്‌റ്റാളുചെയ്യാനാവില്ല."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"പേരുനൽകാത്ത ഫോൾഡർ"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> പ്രവർത്തനരഹിതമാക്കി"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>-ന്, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> അറിയിപ്പുകൾ ഉണ്ട്</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>-ന്, <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> അറിയിപ്പ് ഉണ്ട്</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> എന്ന ആപ്പിന്, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> അറിയിപ്പുകൾ ഉണ്ട്</item>
+      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> എന്ന ആപ്പിന്, <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> അറിയിപ്പുണ്ട്</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"പേജ് %1$d / %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"ഹോം സ്‌ക്രീൻ %1$d / %2$d"</string>
@@ -77,29 +78,22 @@
     <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" msgid="6629239338071103179">"ഫോൾഡർ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"വിജറ്റുകൾ"</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="icon_badging_title" msgid="874121399231955394">"അറിയിപ്പ് ഡോട്ടുകൾ"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"ഓൺ"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"ഓഫ്"</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="icon_badging_service_title" msgid="2309733118428242174">"അറിയിപ്പ് ഡോട്ടുകൾ കാണിക്കുക"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"അറിയിപ്പ് ഡോട്ടുകൾ കാണിക്കുക"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ഹോം സ്ക്രീനിലേക്ക് ഐക്കൺ ചേർക്കുക"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"പുതിയ ആപ്പുകൾക്ക്"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"ഐക്കണിന്റെ ആകാരം മാറ്റുക"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"ഹോം സ്‌ക്രീനിൽ"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"സിസ്‌റ്റം ഡിഫോൾട്ട് ഉപയോഗിക്കുക"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"ചതുരം"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"ചതുരവൃത്തം"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"വൃത്തം"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"കണ്ണുനീര്‍ തുള്ളി"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"ഐക്കൺ ആകാര മാറ്റങ്ങൾ പ്രയോഗിക്കുന്നു"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"അജ്ഞാതം"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"നീക്കംചെയ്യുക"</string>
     <string name="abandoned_search" msgid="891119232568284442">"തിരയുക"</string>
@@ -108,14 +102,13 @@
     <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>
-    <!-- no translation found for widgets_list (796804551140113767) -->
-    <skip />
-    <!-- no translation found for widgets_list_closed (6141506579418771922) -->
-    <skip />
+    <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="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>
@@ -146,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"അറിയിപ്പുകളും ആപ്പുകളും ഓഫാണ്"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"അടയ്ക്കുക"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"അടച്ചു"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"പരാജയപ്പെട്ടു: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml
deleted file mode 100644
index a3507c5..0000000
--- a/res/values-mn-rMN/strings.xml
+++ /dev/null
@@ -1,146 +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">
-    <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
-    <string name="folder_name" msgid="7371454440695724752"></string>
-    <string name="work_folder_name" msgid="3753320833950115786">"Ажил"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"Апп суугаагүй байна."</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"Апп-г ашиглах боломжгүй"</string>
-    <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="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="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="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="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="permlab_install_shortcut" msgid="5632423390354674437">"товчлол суулгах"</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="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>
-    <string name="gadget_setup_text" msgid="8274003207686040488">"Тохируулга"</string>
-    <string name="uninstall_system_app_text" msgid="4172046090762920660">"Энэ апп нь системийн апп ба устгах боломжгүй."</string>
-    <string name="folder_hint_text" msgid="6617836969016293992">"Нэргүй фолдер"</string>
-    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g>-г идэвхгүй болгосон"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> мэдэгдэл байна</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> мэдэгдэл байна</item>
-    </plurals>
-    <string name="default_scroll_format" msgid="7475544710230993317">"%2$d-н %1$d хуудас"</string>
-    <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d-н Нүүр дэлгэц %1$d"</string>
-    <string name="workspace_new_page" msgid="257366611030256142">"Шинэ үндсэн нүүр хуудас"</string>
-    <string name="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" msgid="6629239338071103179">"Фолдер: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Виджет"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"Ханын зураг"</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="icon_badging_title" msgid="874121399231955394">"Мэдэгдлийн цэг"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Асаалттай"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Унтраалттай"</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="icon_badging_service_title" msgid="2309733118428242174">"Мэдэгдлийн цэгийг харуулах"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Нүүр хуудаст дүрс тэмдэг нэмэх"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Шинэ аппад зориулсан"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Дүрс тэмдгийн хэлбэрийг өөрчлөх"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"Үндсэн нүүр хэсэгт"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Системийн өгөгдмөл тохиргоог ашиглах"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Дөрвөлжин"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Мохоо өнцөгтэй дөрвөлжин"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Дугуй"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Дусал"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Дүрс тэмдгийн хэлбэрийг өөрчилж байна"</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_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="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="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="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>
-    <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_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g>-н <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> товчлол"</string>
-    <string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> товчлол болон <xliff:g id="APP_NAME">%3$s</xliff:g>-н <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> мэдэгдэл"</string>
-    <string name="action_dismiss_notification" msgid="5909461085055959187">"Хаах"</string>
-    <string name="notification_dismissed" msgid="6002233469409822874">"Мэдэгдлийг хаасан"</string>
-    <string name="all_apps_personal_tab" msgid="4190252696685155002">"Хувийн"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"Ажил"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"Ажлын профайл"</string>
-    <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Ажлын аппыг эндээс олно уу"</string>
-    <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Ажлын апп тус бүр тэмдэгтэй ба эдгээрийг танай байгууллагаас аюулгүй байлгадаг. Аппуудад хялбар хандахын тулд тэдгээрийг Үндсэн нүүр хэсэгт зөөнө үү."</string>
-    <string name="work_mode_on_label" msgid="4781128097185272916">"Танай байгууллагаас удирддаг"</string>
-    <string name="work_mode_off_label" msgid="3194894777601421047">"Мэдэгдэл, апп унтраалттай байна"</string>
-    <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Хаах"</string>
-    <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Хаасан"</string>
-</resources>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 9fbb1e5..ab02ac6 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,9 +65,9 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Энэ апп нь системийн апп ба устгах боломжгүй."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Нэргүй фолдер"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g>-г идэвхгүй болгосон"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> мэдэгдэл байна</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> мэдэгдэл байна</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> мэдэгдэлтэй байна</item>
+      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> мэдэгдэлтэй байна</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"%2$d-н %1$d хуудас"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d-н Нүүр дэлгэц %1$d"</string>
@@ -79,29 +80,22 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Фолдер: <xliff:g id="NAME">%1$s</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="icon_badging_title" msgid="874121399231955394">"Мэдэгдлийн цэг"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Асаалттай"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Унтраалттай"</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="icon_badging_service_title" msgid="2309733118428242174">"Мэдэгдлийн цэгийг харуулах"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Мэдэгдлийн цэгийг харуулах"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Нүүр хуудаст дүрс тэмдэг нэмэх"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Шинэ аппад зориулсан"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Дүрс тэмдгийн хэлбэрийг өөрчлөх"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"Үндсэн нүүр хэсэгт"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Системийн өгөгдмөл тохиргоог ашиглах"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Дөрвөлжин"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Мохоо өнцөгтэй дөрвөлжин"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Дугуй"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Дусал"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Дүрс тэмдгийн хэлбэрийг өөрчилж байна"</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>
@@ -110,10 +104,11 @@
     <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="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>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Мэдэгдэл, апп унтраалттай байна"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Хаах"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Хаасан"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"Амжилтгүй болсон: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-mr-rIN/strings.xml b/res/values-mr-rIN/strings.xml
deleted file mode 100644
index bf1e188..0000000
--- a/res/values-mr-rIN/strings.xml
+++ /dev/null
@@ -1,146 +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">
-    <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
-    <string name="folder_name" msgid="7371454440695724752"></string>
-    <string name="work_folder_name" msgid="3753320833950115786">"कार्य"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"अॅप इंस्टॉल केलेला नाही."</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"अॅप उपलब्ध नाही"</string>
-    <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="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="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="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="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="permlab_install_shortcut" msgid="5632423390354674437">"शॉर्टकट स्‍थापित करा"</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="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>
-    <string name="gadget_setup_text" msgid="8274003207686040488">"सेटअप"</string>
-    <string name="uninstall_system_app_text" msgid="4172046090762920660">"हा सिस्टम अॅप आहे आणि अनइंस्टॉल केला जाऊ शकत नाही."</string>
-    <string name="folder_hint_text" msgid="6617836969016293992">"अनामित फोल्डर"</string>
-    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> अक्षम केला आहे"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, कडे <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> सूचना आहे</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, कडे <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> सूचना आहेत</item>
-    </plurals>
-    <string name="default_scroll_format" msgid="7475544710230993317">"%2$d पैकी %1$d पृष्ठ"</string>
-    <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d पैकी %1$d मुख्य स्क्रीन"</string>
-    <string name="workspace_new_page" msgid="257366611030256142">"नवीन मुख्य स्क्रीन पृष्ठ"</string>
-    <string name="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" msgid="6629239338071103179">"फोल्डर: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"विजेट"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"वॉलपेपर"</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="icon_badging_title" msgid="874121399231955394">"सूचना बिंदू"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"चालू"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"बंद"</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="icon_badging_service_title" msgid="2309733118428242174">"सूचना बिंदू दाखवा"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"होम स्क्रीनवर आयकन जोडा"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"नवीन अॅप्ससाठी"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"चिन्हाचा आकार बदला"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"होम स्क्रीनवर"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"सिस्‍टमचे डीफॉल्‍ट वापरा"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"चौरस"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"गोलाकार चौरस"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"वर्तुळ"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"अश्रू"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"चिन्हाचा आकार बदल लागू करत आहे"</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_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="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="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="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>
-    <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_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> साठी <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> शॉर्टकट"</string>
-    <string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g>साठी <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> शॉर्टकट आणि <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> सूचना"</string>
-    <string name="action_dismiss_notification" msgid="5909461085055959187">"डिसमिस करा"</string>
-    <string name="notification_dismissed" msgid="6002233469409822874">"सूचना डिसमिस केली"</string>
-    <string name="all_apps_personal_tab" msgid="4190252696685155002">"वैयक्तिक"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"कार्यालय"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"कार्य प्रोफाइल"</string>
-    <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"कामाची अ‍ॅप्स येथे मिळवा"</string>
-    <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"प्रत्येक कार्य अ‍ॅपला एक बॅज असतो आणि तो तुमच्या संस्थेकडून सुरक्षित ठेवला जातो. अधिक सहज अ‍ॅक्सेससाठी अ‍ॅप्स तुमच्या होम स्क्रीनवर हलवा."</string>
-    <string name="work_mode_on_label" msgid="4781128097185272916">"तुमच्या संस्थेकडून व्यवस्थापित"</string>
-    <string name="work_mode_off_label" msgid="3194894777601421047">"सूचना आणि अॅप्स बंद आहेत"</string>
-    <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"बंद करा"</string>
-    <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"बंद केले"</string>
-</resources>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index a297e06..19c0697 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -22,8 +22,8 @@
     <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="work_folder_name" msgid="3753320833950115786">"कार्य"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"अॅप इंस्टॉल केलेला नाही."</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"अॅप उपलब्ध नाही"</string>
+    <string name="activity_not_found" msgid="8071924732094499514">"अ‍ॅप इंस्टॉल केलेला नाही."</string>
+    <string name="activity_not_available" msgid="7456344436509528827">"अ‍ॅप उपलब्ध नाही"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"डाउनलोड केलेला अ‍ॅप सुरक्षित मोड मध्‍ये अक्षम केला"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"विजेट सुरक्षित मोडमध्ये अक्षम झाले"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"शॉर्टकट उपलब्ध नाही"</string>
@@ -34,11 +34,12 @@
     <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>
     <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>
@@ -50,23 +51,23 @@
     <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="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>
-    <string name="uninstall_system_app_text" msgid="4172046090762920660">"हा सिस्टम अॅप आहे आणि अनइंस्टॉल केला जाऊ शकत नाही."</string>
+    <string name="uninstall_system_app_text" msgid="4172046090762920660">"हा सिस्टम अ‍ॅप आहे आणि अनइंस्टॉल केला जाऊ शकत नाही."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"अनामित फोल्डर"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> अक्षम केला आहे"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, कडे <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> सूचना आहे</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, कडे <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> सूचना आहेत</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>साठी <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> सूचना आहेत</item>
+      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>साठी<xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g>सूचना आहे</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"%2$d पैकी %1$d पृष्ठ"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d पैकी %1$d मुख्य स्क्रीन"</string>
@@ -79,32 +80,25 @@
     <string name="folder_name_format" msgid="6629239338071103179">"फोल्डर: <xliff:g id="NAME">%1$s</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="icon_badging_title" msgid="874121399231955394">"सूचना बिंदू"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"चालू"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"बंद"</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="icon_badging_service_title" msgid="2309733118428242174">"सूचना बिंदू दाखवा"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"सूचना बिंदू दाखवा"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"होम स्क्रीनवर आयकन जोडा"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"नवीन अॅप्ससाठी"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"चिन्हाचा आकार बदला"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"होम स्क्रीनवर"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"सिस्‍टमचे डीफॉल्‍ट वापरा"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"चौरस"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"गोलाकार चौरस"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"वर्तुळ"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"अश्रू"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"चिन्हाचा आकार बदल लागू करत आहे"</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="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_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>
@@ -114,6 +108,7 @@
     <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>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"सूचना आणि अॅप्स बंद आहेत"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"बंद करा"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"बंद केले"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"हे करता आले नाही: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml
deleted file mode 100644
index 3c223b6..0000000
--- a/res/values-ms-rMY/strings.xml
+++ /dev/null
@@ -1,146 +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">
-    <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
-    <string name="folder_name" msgid="7371454440695724752"></string>
-    <string name="work_folder_name" msgid="3753320833950115786">"Kerja"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"Apl tidak dipasang."</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"Apl tidak tersedia"</string>
-    <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="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>
-    <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="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="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>
-    <string name="install_drop_target_label" msgid="2539096853673231757">"Pasang"</string>
-    <string name="permlab_install_shortcut" msgid="5632423390354674437">"pasang pintasan"</string>
-    <string name="permdesc_install_shortcut" msgid="923466509822011139">"Membenarkan apl menambah pintasan tanpa campur tangan pengguna."</string>
-    <string name="permlab_read_settings" msgid="1941457408239617576">"baca tetapan dan pintasan Laman Utama"</string>
-    <string name="permdesc_read_settings" msgid="5833423719057558387">"Membenarkan apl membaca tetapan dan pintasan di Laman Utama."</string>
-    <string name="permlab_write_settings" msgid="3574213698004620587">"tulis tetapan dan pintasan Laman Utama"</string>
-    <string name="permdesc_write_settings" msgid="5440712911516509985">"Membenarkan apl menukar tetapan dan pintasan di Laman Utama."</string>
-    <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> tidak dibenarkan membuat panggilan telefon"</string>
-    <string name="gadget_error_text" msgid="6081085226050792095">"Masalah memuatkan widget"</string>
-    <string name="gadget_setup_text" msgid="8274003207686040488">"Persediaan"</string>
-    <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ini ialah apl sistem dan tidak boleh dinyahpasang."</string>
-    <string name="folder_hint_text" msgid="6617836969016293992">"Folder Tanpa Nama"</string>
-    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> dilumpuhkan"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, mempunyai <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> pemberitahuan</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, mempunyai <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> pemberitahuan</item>
-    </plurals>
-    <string name="default_scroll_format" msgid="7475544710230993317">"Halaman %1$d daripada %2$d"</string>
-    <string name="workspace_scroll_format" msgid="8458889198184077399">"Skrin Laman Utama %1$d daripada %2$d"</string>
-    <string name="workspace_new_page" msgid="257366611030256142">"Halaman skrin utama baharu"</string>
-    <string name="folder_opened" msgid="94695026776264709">"Folder dibuka, <xliff:g id="WIDTH">%1$d</xliff:g> kali <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="4625795376335528256">"Ketik untuk menutup folder"</string>
-    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Ketik untuk menyimpan penamaan semula"</string>
-    <string name="folder_closed" msgid="4100806530910930934">"Folder ditutup"</string>
-    <string name="folder_renamed" msgid="1794088362165669656">"Folder dinamakan semula kepada <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Widget"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"Kertas dinding"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Tetapan laman utama"</string>
-    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Dilumpuhkan oleh pentadbir anda"</string>
-    <string name="allow_rotation_title" msgid="7728578836261442095">"Benarkan putaran Skrin Utama"</string>
-    <string name="allow_rotation_desc" msgid="8662546029078692509">"Apabila telefon diputar"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Titik pemberitahuan"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Hidup"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Mati"</string>
-    <string name="title_missing_notification_access" msgid="7503287056163941064">"Akses pemberitahuan diperlukan"</string>
-    <string name="msg_missing_notification_access" msgid="281113995110910548">"Untuk menunjukkan Titik Pemberitahuan, hidupkan pemberitahuan apl untuk <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="title_change_settings" msgid="1376365968844349552">"Tukar tetapan"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Tunjukkan titik pemberitahuan"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Tambahkan ikon pada Skrin Utama"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Untuk apl baharu"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Tukar bentuk ikon"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"pada Skrin Utama"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Gunakan lalai sistem"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Segi empat sama"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Segi empat berbucu bulat"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Bulatan"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Titisan air mata"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Menggunakan perubahan bentuk ikon"</string>
-    <string name="package_state_unknown" msgid="7592128424511031410">"Tidak diketahui"</string>
-    <string name="abandoned_clean_this" msgid="7610119707847920412">"Alih keluar"</string>
-    <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_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="action_add_to_workspace" msgid="8902165848117513641">"Tambahkan pada Skrin Utama"</string>
-    <string name="action_move_here" msgid="2170188780612570250">"Alihkan item ke sini"</string>
-    <string name="item_added_to_workspace" msgid="4211073925752213539">"Item ditambahkan pada skrin utama"</string>
-    <string name="item_removed" msgid="851119963877842327">"Item dialih keluar"</string>
-    <string name="action_move" msgid="4339390619886385032">"Alihkan Item"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Alihkan ke baris <xliff:g id="NUMBER_0">%1$s</xliff:g> lajur <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
-    <string name="move_to_position" msgid="6750008980455459790">"Alihkan ke kedudukan <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
-    <string name="move_to_hotseat_position" msgid="6295412897075147808">"Alihkan ke kedudukan kegemaran <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
-    <string name="item_moved" msgid="4606538322571412879">"Item dialihkan"</string>
-    <string name="add_to_folder" msgid="9040534766770853243">"Tambahkan pada folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="add_to_folder_with_app" msgid="4534929978967147231">"Tambahkan pada folder dengan <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="added_to_folder" msgid="4793259502305558003">"Item ditambahkan pada folder"</string>
-    <string name="create_folder_with" msgid="4050141361160214248">"Buat folder dengan: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="folder_created" msgid="6409794597405184510">"Folder dibuat"</string>
-    <string name="action_move_to_workspace" msgid="1603837886334246317">"Alihkan ke Skrin Utama"</string>
-    <string name="action_resize" msgid="1802976324781771067">"Ubah saiz"</string>
-    <string name="action_increase_width" msgid="8773715375078513326">"Tambahkan kelebaran"</string>
-    <string name="action_increase_height" msgid="459390020612501122">"Tambahkan ketinggian"</string>
-    <string name="action_decrease_width" msgid="1374549771083094654">"Kurangkan kelebaran"</string>
-    <string name="action_decrease_height" msgid="282377193880900022">"Kurangkan ketinggian"</string>
-    <string name="widget_resized" msgid="9130327887929620">"Saiz widget diubah menjadi <xliff:g id="NUMBER_0">%1$s</xliff:g> lebar <xliff:g id="NUMBER_1">%2$s</xliff:g> tinggi"</string>
-    <string name="action_deep_shortcut" msgid="2864038805849372848">"Pintasan"</string>
-    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> pintasan untuk <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
-    <string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> pintasan dan <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> pemberitahuan untuk <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
-    <string name="action_dismiss_notification" msgid="5909461085055959187">"Ketepikan"</string>
-    <string name="notification_dismissed" msgid="6002233469409822874">"Pemberitahuan diketepikan"</string>
-    <string name="all_apps_personal_tab" msgid="4190252696685155002">"Peribadi"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"Kerja"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil kerja"</string>
-    <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Temui apl kerja di sini"</string>
-    <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Setiap apl kerja terdapat lencana dan dilindungi oleh organisasi anda. Alihkan apl ke Skrin Utama untuk akses yang lebih mudah."</string>
-    <string name="work_mode_on_label" msgid="4781128097185272916">"Diurus oleh organisasi anda"</string>
-    <string name="work_mode_off_label" msgid="3194894777601421047">"Pemberitahuan dan apl dimatikan"</string>
-    <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Tutup"</string>
-    <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Ditutup"</string>
-</resources>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 71047fb..7d05412 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,7 +65,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ini ialah apl sistem dan tidak boleh dinyahpasang."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Folder Tanpa Nama"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> dilumpuhkan"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, mempunyai <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> pemberitahuan</item>
       <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, mempunyai <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> pemberitahuan</item>
     </plurals>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</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>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Dilumpuhkan oleh pentadbir anda"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Benarkan putaran Skrin Utama"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Apabila telefon diputar"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Titik pemberitahuan"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Hidup"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Mati"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Titik pemberitahuan"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Hidup"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Mati"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Akses pemberitahuan diperlukan"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Untuk menunjukkan Titik Pemberitahuan, hidupkan pemberitahuan apl untuk <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Tukar tetapan"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Tunjukkan titik pemberitahuan"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Tunjukkan titik pemberitahuan"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Tambahkan ikon pada Skrin Utama"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Untuk apl baharu"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Tukar bentuk ikon"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"pada Skrin Utama"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Gunakan lalai sistem"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Segi empat sama"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Segi empat berbucu bulat"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Bulatan"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Titisan air mata"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Menggunakan perubahan bentuk ikon"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Tidak diketahui"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Alih keluar"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Carian"</string>
@@ -114,6 +108,7 @@
     <string name="action_move_here" msgid="2170188780612570250">"Alihkan item ke sini"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Item ditambahkan pada skrin utama"</string>
     <string name="item_removed" msgid="851119963877842327">"Item dialih keluar"</string>
+    <string name="undo" msgid="4151576204245173321">"Buat asal"</string>
     <string name="action_move" msgid="4339390619886385032">"Alihkan Item"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Alihkan ke baris <xliff:g id="NUMBER_0">%1$s</xliff:g> lajur <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Alihkan ke kedudukan <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Pemberitahuan dan apl dimatikan"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Tutup"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Ditutup"</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-rMM/strings.xml b/res/values-my-rMM/strings.xml
deleted file mode 100644
index 59ac4ca..0000000
--- a/res/values-my-rMM/strings.xml
+++ /dev/null
@@ -1,146 +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">
-    <string name="app_name" msgid="649227358658669779">"ဖွင့်တင်စက်၃"</string>
-    <string name="folder_name" msgid="7371454440695724752"></string>
-    <string name="work_folder_name" msgid="3753320833950115786">"အလုပ်"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"အက်ပ်မထည့်သွင်းထားပါ"</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"အက်ပ်လက်လှမ်း မမှီပါ"</string>
-    <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="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="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="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="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="permlab_install_shortcut" msgid="5632423390354674437">"အတိုကောက်မှတ်သားမှုများအား ထည့်သွင်းခြင်း"</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="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>
-    <string name="gadget_setup_text" msgid="8274003207686040488">"စဖွင့်သတ်မှတ်ရန်"</string>
-    <string name="uninstall_system_app_text" msgid="4172046090762920660">"ဤအပ်ပလီကေးရှင်းမှာ စစ်စတန်ပိုင်းဆိုင်ရာ အပ်ပလီကေးရှင်းဖြစ်ပါသည်။ ထုတ်ပစ်၍ မရပါ"</string>
-    <string name="folder_hint_text" msgid="6617836969016293992">"အမည်မရှိအကန့်"</string>
-    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> ကို ပိတ်ထားသည်"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> တွင် အကြောင်းကြားချက် <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ခု ရှိသည်</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> တွင် အကြောင်းကြားချက် <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> ခု ရှိသည်</item>
-    </plurals>
-    <string name="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_name_format" msgid="6629239338071103179">"အကန့်အမည်: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"ဝိဂျက်များ"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"နောက်ခံများ"</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="icon_badging_title" msgid="874121399231955394">"အကြောင်းကြားချက်အမှတ်အသားများ"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"ဖွင့်ထားသည်"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"ပိတ်ထားသည်"</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="icon_badging_service_title" msgid="2309733118428242174">"အကြောင်းကြားချက် အမှတ်အသားများကို ပြရန်"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ပင်မစာမျက်နှာသို့ သင်္ကေတပုံ ထည့်ရန်"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"အက်ပ်အသစ်များအတွက်"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"သင်္ကေတပုံစံကို ပြောင်းရန်"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"\'ပင်မမျက်နှာပြင်\' ပေါ်တွင်"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"စနစ်၏ မူရင်းပုံကို အသုံးပြုရန်"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"လေးထောင့်"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"စတုရန်းမကျ စက်ဝိုင်းမကျပုံ"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"စက်ဝိုင်း"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"မျက်ရည်စက်ပုံ"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"သင်္ကေတပုံစံကို ပြောင်းလဲနေသည်"</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_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="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="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="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>
-    <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">"Widget အား အကျယ် <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_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> အတွက် အမြန်နည်း <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> ခု"</string>
-    <string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> အတွက် ဖြတ်လမ်းလင့်ခ် <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> နှင့် အကြောင်းကြားချက် <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> ခု"</string>
-    <string name="action_dismiss_notification" msgid="5909461085055959187">"ပယ်ရန်"</string>
-    <string name="notification_dismissed" msgid="6002233469409822874">"အသိပေးချက်ကို ဖယ်ထုတ်ပြီးပါပြီ"</string>
-    <string name="all_apps_personal_tab" msgid="4190252696685155002">"ကိုယ်ပိုင်"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"အလုပ်"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"အလုပ်ပရိုဖိုင်"</string>
-    <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"အလုပ်အက်ပ်များကို ဤနေရာတွင်ရှာဖွေပါ"</string>
-    <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"အလုပ်အက်ပ်တိုင်းတွင် တံဆိပ် တစ်ခုစီရှိပြီး သင်၏ အဖွဲ့အစည်းက လုံခြုံအောင် ထားရှိပါသည်။ အသုံးပြုရ ပိုမိုလွယ်ကူစေရန် အက်ပ်များကို သင်၏ ပင်မမျက်နှာပြင်သို့ ရွှေ့ပါ။"</string>
-    <string name="work_mode_on_label" msgid="4781128097185272916">"သင်၏ အဖွဲ့အစည်းက စီမံခန့်ခွဲထားပါသည်"</string>
-    <string name="work_mode_off_label" msgid="3194894777601421047">"အကြောင်းကြားချက်များနှင့် အက်ပ်များကို ပိတ်ထားသည်"</string>
-    <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"ပိတ်ရန်"</string>
-    <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"ပိတ်ထားသည်"</string>
-</resources>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index 6c26185..fd1f7d9 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -49,7 +50,7 @@
     <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="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="permlab_install_shortcut" msgid="5632423390354674437">"အတိုကောက်မှတ်သားမှုများအား ထည့်သွင်းခြင်း"</string>
@@ -64,43 +65,36 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"ဤအပ်ပလီကေးရှင်းမှာ စစ်စတန်ပိုင်းဆိုင်ရာ အပ်ပလီကေးရှင်းဖြစ်ပါသည်။ ထုတ်ပစ်၍ မရပါ"</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"အမည်မရှိအကန့်"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> ကို ပိတ်ထားသည်"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> တွင် အကြောင်းကြားချက် <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ခု ရှိသည်</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> တွင် အကြောင်းကြားချက် <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> ခု ရှိသည်</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> တွင် သတိပေးချက် <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ခု ရှိသည်</item>
+      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> တွင် သတိပေးချက် <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> ခု ရှိသည်</item>
     </plurals>
     <string name="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_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" msgid="6629239338071103179">"အကန့်အမည်: <xliff:g id="NAME">%1$s</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="icon_badging_title" msgid="874121399231955394">"အကြောင်းကြားချက်အမှတ်အသားများ"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"ဖွင့်ထားသည်"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"ပိတ်ထားသည်"</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="icon_badging_service_title" msgid="2309733118428242174">"အကြောင်းကြားချက် အမှတ်အသားများကို ပြရန်"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"သတိပေးချက် အစက်များ ပြရန်"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ပင်မစာမျက်နှာသို့ သင်္ကေတပုံ ထည့်ရန်"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"အက်ပ်အသစ်များအတွက်"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"သင်္ကေတပုံစံကို ပြောင်းရန်"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"\'ပင်မမျက်နှာပြင်\' ပေါ်တွင်"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"စနစ်၏ မူရင်းပုံကို အသုံးပြုရန်"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"လေးထောင့်"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"စတုရန်းမကျ စက်ဝိုင်းမကျပုံ"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"စက်ဝိုင်း"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"မျက်ရည်စက်ပုံ"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"သင်္ကေတပုံစံကို ပြောင်းလဲနေသည်"</string>
-    <string name="package_state_unknown" msgid="7592128424511031410">"မသိရ"</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>
@@ -114,6 +108,7 @@
     <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>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"အကြောင်းကြားချက်များနှင့် အက်ပ်များကို ပိတ်ထားသည်"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"ပိတ်ရန်"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"ပိတ်ထားသည်"</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 67a912b..2257367 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,7 +65,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Dette er en systemapp som ikke kan avinstalleres."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Mappe uten navn"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Slo av <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> har <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> varsler</item>
       <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> har <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> varsel</item>
     </plurals>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Mappe: <xliff:g id="NAME">%1$s</xliff:g>"</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>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administratoren har slått av funksjonen"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Tillat rotasjon av startskjermen"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Når telefonen roteres"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Varselsprikker"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"På"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Av"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Varselsprikker"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"På"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Av"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Tilgang til varsler er nødvendig"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Slå på appvarsler for <xliff:g id="NAME">%1$s</xliff:g> for å vise varselsprikker"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Endre innstillingene"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Vis varselsprikker"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Vis varselsprikker"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Legg til ikon på startsiden"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"For nye apper"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Endre formen på ikonet"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"på startskjermen"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Bruk systemstandard"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Superellipse"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Sirkel"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Dråpe"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Aktiverer endringer av formen på ikonet"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Ukjent"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Fjern"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Søk"</string>
@@ -114,6 +108,7 @@
     <string name="action_move_here" msgid="2170188780612570250">"Flytt elementet hit"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Elementet er lagt til på startskjermen"</string>
     <string name="item_removed" msgid="851119963877842327">"Elementet er fjernet"</string>
+    <string name="undo" msgid="4151576204245173321">"Angre"</string>
     <string name="action_move" msgid="4339390619886385032">"Flytt elementet"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Flytt til rad <xliff:g id="NUMBER_0">%1$s</xliff:g>, kolonne <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Flytt til posisjon <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Varsler og apper er slått av"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Lukk"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Lukket"</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-rNP/strings.xml b/res/values-ne-rNP/strings.xml
deleted file mode 100644
index a8a64f1..0000000
--- a/res/values-ne-rNP/strings.xml
+++ /dev/null
@@ -1,146 +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">
-    <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
-    <string name="folder_name" msgid="7371454440695724752"></string>
-    <string name="work_folder_name" msgid="3753320833950115786">"कार्य"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"अनुप्रयोग स्थापित छैन।"</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"अनुप्रयोग उपलब्ध छैन"</string>
-    <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="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="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="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="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="permlab_install_shortcut" msgid="5632423390354674437">"सर्टकट स्थापना गर्नेहोस्"</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="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>
-    <string name="gadget_setup_text" msgid="8274003207686040488">"सेटअप"</string>
-    <string name="uninstall_system_app_text" msgid="4172046090762920660">"यो प्रणाली अनुप्रयोग हो र यसलाई स्थापना रद्द गर्न सकिँदैन।"</string>
-    <string name="folder_hint_text" msgid="6617836969016293992">"बेनाम फोल्डर"</string>
-    <string name="disabled_app_label" msgid="6673129024321402780">"असक्षम पारिएको <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, यसमा <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> सूचनाहरू छन्‌</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, यसमा <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> सूचना छ</item>
-    </plurals>
-    <string name="default_scroll_format" msgid="7475544710230993317">"पृष्ठ %2$d को %1$d"</string>
-    <string name="workspace_scroll_format" msgid="8458889198184077399">"गृह स्क्रिन %1$d को %2$d"</string>
-    <string name="workspace_new_page" msgid="257366611030256142">"नयाँ गृह स्क्रिन पृष्ठ"</string>
-    <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" msgid="6629239338071103179">"फोल्डर: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"विजेटहरू"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"वालपेपरहरु"</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="icon_badging_title" msgid="874121399231955394">"सूचनाको प्रतीक जनाउने थोप्लोहरू"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"सक्रिय छ"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"निष्क्रिय छ"</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="icon_badging_service_title" msgid="2309733118428242174">"सूचनाको प्रतीक जनाउने थोप्लाहरू देखाउनुहोस्"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"गृह स्क्रिनमा आइकन थप्नुहोस्"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"नयाँ अनुप्रयोगका लागि"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"आइकनको आकार परिवर्तन गर्नुहोस्"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"गृह स्क्रिनमा"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"प्रणालीको पूर्वनिर्धारित सेटिङ प्रयोग गर्नुहोस्"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"वर्ग"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"वर्गाकार वृत्त"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"वृत्त"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"आँसुको थोपा"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"आइकनको आकारमा गरिएका परिवर्तनहरू लागू गरिँदैछन्"</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_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="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="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="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>
-    <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_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> का <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> सर्टकटहरू"</string>
-    <string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> का <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> सर्टकट र <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> सूचनाहरू"</string>
-    <string name="action_dismiss_notification" msgid="5909461085055959187">"खारेज गर्नुहोस्"</string>
-    <string name="notification_dismissed" msgid="6002233469409822874">"सूचना खारेज गरियो"</string>
-    <string name="all_apps_personal_tab" msgid="4190252696685155002">"व्यक्तिगत"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"कार्यसम्बन्धी"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"कार्य प्रोफाइल"</string>
-    <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"कार्यसम्बन्धी अनुप्रयोगहरू यहाँ प्राप्त गर्नुहोस्"</string>
-    <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"कार्यसम्बन्धी प्रत्येक अनुप्रयोगमा एउटा ब्याज छ र तपाईंको संगठनले यसलाई सुरक्षित राखेको छ । अझ सजिलो गरी पहुँच राख्नका लागि अनुप्रयोगहरूलाई आफ्नो गृहस्क्रिनमा सार्नुहोस्‌।"</string>
-    <string name="work_mode_on_label" msgid="4781128097185272916">"तपाईंको सङ्गठनले व्यवस्थापन गरेको"</string>
-    <string name="work_mode_off_label" msgid="3194894777601421047">"सूचना र अनुप्रयोगहरू निष्क्रिय छन्‌"</string>
-    <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"बन्द गर्नुहोस्"</string>
-    <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"बन्द गरियो"</string>
-</resources>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 50c4279..1e7aee9 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -39,11 +39,12 @@
     <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="hotseat_out_of_space" msgid="7448809638125333693">"मनपर्ने ट्रे अब कुनै ठाँउ छैन"</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>
@@ -64,9 +65,9 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"यो प्रणाली अनुप्रयोग हो र यसलाई स्थापना रद्द गर्न सकिँदैन।"</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"बेनाम फोल्डर"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"असक्षम पारिएको <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, यसमा <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> सूचनाहरू छन्‌</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, यसमा <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> सूचना छ</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, का <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> सूचनाहरू छन्</item>
+      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, को <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> सूचना छ</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"पृष्ठ %2$d को %1$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"गृह स्क्रिन %1$d को %2$d"</string>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"फोल्डर: <xliff:g id="NAME">%1$s</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="icon_badging_title" msgid="874121399231955394">"सूचनाको प्रतीक जनाउने थोप्लोहरू"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"सक्रिय छ"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"निष्क्रिय छ"</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="icon_badging_service_title" msgid="2309733118428242174">"सूचनाको प्रतीक जनाउने थोप्लाहरू देखाउनुहोस्"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"सूचनाको प्रतीक जनाउने थोप्लाहरू देखाउनुहोस्"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"गृह स्क्रिनमा आइकन थप्नुहोस्"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"नयाँ अनुप्रयोगका लागि"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"आइकनको आकार परिवर्तन गर्नुहोस्"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"गृह स्क्रिनमा"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"प्रणालीको पूर्वनिर्धारित सेटिङ प्रयोग गर्नुहोस्"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"वर्ग"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"वर्गाकार वृत्त"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"वृत्त"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"आँसुको थोपा"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"आइकनको आकारमा गरिएका परिवर्तनहरू लागू गरिँदैछन्"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"अज्ञात"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"हटाउनुहोस्"</string>
     <string name="abandoned_search" msgid="891119232568284442">"खोजी गर्नुहोस्"</string>
@@ -108,18 +102,17 @@
     <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>
-    <!-- no translation found for widgets_list (796804551140113767) -->
-    <skip />
-    <!-- no translation found for widgets_list_closed (6141506579418771922) -->
-    <skip />
+    <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="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="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>
@@ -146,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"सूचना र अनुप्रयोगहरू निष्क्रिय छन्‌"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"बन्द गर्नुहोस्"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"बन्द गरियो"</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-v26/styles.xml
new file mode 100644
index 0000000..510e1f4
--- /dev/null
+++ b/res/values-night-v26/styles.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright (C) 2018 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+
+<resources>
+
+    <style name="AppItemActivityTheme" parent="@android:style/Theme.DeviceDefault.Dialog.Alert">
+        <item name="widgetsTheme">@style/WidgetContainerTheme.Dark</item>
+    </style>
+
+</resources>
\ No newline at end of file
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 64f4d79..ec30d8c 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -33,12 +33,13 @@
     <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="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 op een item en houd dit vast om het handmatig te plaatsen"</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>
     <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>
@@ -64,9 +65,9 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Dit is een systeemapp die niet kan worden verwijderd."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Naamloze map"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> is uitgeschakeld"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> heeft <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> meldingen</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> heeft <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> melding</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, heeft <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> meldingen</item>
+      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, heeft <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> melding</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"Pagina %1$d van %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Startscherm %1$d van %2$d"</string>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Map: <xliff:g id="NAME">%1$s</xliff:g>"</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 startscherm"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Uitgeschakeld 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="icon_badging_title" msgid="874121399231955394">"Meldingsstipjes"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Aan"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Uit"</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="title_change_settings" msgid="1376365968844349552">"Instellingen wijzigen"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Meldingsstipjes weergeven"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Meldingsstipjes weergeven"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Pictogram toevoegen aan startscherm"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Voor nieuwe apps"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Vorm van pictogram wijzigen"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"op het startscherm"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Systeemstandaard gebruiken"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Vierkant"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Squircle"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Cirkel"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Traan"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Wijzigingen in vorm van pictogram toepassen"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Onbekend"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Verwijderen"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Zoeken"</string>
@@ -114,6 +108,7 @@
     <string name="action_move_here" msgid="2170188780612570250">"Item hier naartoe verplaatsen"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Item toegevoegd aan startscherm"</string>
     <string name="item_removed" msgid="851119963877842327">"Item verwijderd"</string>
+    <string name="undo" msgid="4151576204245173321">"Ongedaan maken"</string>
     <string name="action_move" msgid="4339390619886385032">"Item verplaatsen"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Verplaatsen naar rij <xliff:g id="NUMBER_0">%1$s</xliff:g>, kolom <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Verplaatsen naar positie <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Meldingen en apps zijn uitgeschakeld"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Sluiten"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Gesloten"</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 5fa6607..4ddc903 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,9 +65,9 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"ଏହା ଏକ ସିଷ୍ଟମ୍‌ ଆପ୍‌ ଅଟେ ଏବଂ ଏହା ଅନଇନଷ୍ଟଲ୍‌ କରାଯାଇ ପାରିବ ନାହିଁ।"</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"ବେନାମୀ ଫୋଲ୍ଡର୍‌"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> ଅକ୍ଷମ କରାଗଲା"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ରେ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ଅଛି</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, ରେ <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> ବିଜ୍ଞପ୍ତି ଅଛି</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>ଟି ବିଜ୍ଞପ୍ତି ରହିଛି</item>
+      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g>ଟି ବିଜ୍ଞପ୍ତି ରହିଛି</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"ମୋଟ %2$dରୁ %1$d ନମ୍ବର ପୃଷ୍ଠା"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$dରୁ %1$d ହୋମ୍‌ ସ୍କ୍ରୀନ୍"</string>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"ଫୋଲ୍ଡର: <xliff:g id="NAME">%1$s</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="icon_badging_title" msgid="874121399231955394">"ବିଜ୍ଞପ୍ତି ବିନ୍ଦୁଗୁଡ଼ିକ"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"ଅନ୍"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"ଅଫ୍‌"</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="icon_badging_service_title" msgid="2309733118428242174">"ବିଜ୍ଞପ୍ତି ଡଟ୍‌ଗୁଡ଼ିକୁ ଦେଖାନ୍ତୁ"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"ବିଜ୍ଞପ୍ତି ଡଟ୍‌ଗୁଡ଼ିକୁ ଦେଖାନ୍ତୁ"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ହୋମ୍‌ ସ୍କ୍ରୀନରେ ଆଇକନ୍‌କୁ ଯୋଡ଼ନ୍ତୁ"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"ନୂଆ ଆପ୍‌ ପାଇଁ"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"ଆଇକନ୍‌ର ଆକାର ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"ହୋମ୍ ସ୍କ୍ରୀନ୍ ଉପରେ"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"ସିଷ୍ଟମ ଡିଫଲ୍ଟ ବ୍ୟବହାର କରନ୍ତୁ"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"ବର୍ଗାକାର"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"ବର୍ଗାକାରର ବୃତ୍ତ"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"ବୃତ୍ତ"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"ଟିଅରଡ୍ରପ୍‌"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"ଆଇକନ୍‌ ଆକାର ପରିବର୍ତ୍ତନ ଲାଗୁ କରୁଛି"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"ଅଜଣା"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ବାହାର କରନ୍ତୁ"</string>
     <string name="abandoned_search" msgid="891119232568284442">"ସର୍ଚ୍ଚ କରନ୍ତୁ"</string>
@@ -108,14 +102,13 @@
     <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>
-    <!-- no translation found for widgets_list (796804551140113767) -->
-    <skip />
-    <!-- no translation found for widgets_list_closed (6141506579418771922) -->
-    <skip />
+    <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="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>
@@ -146,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"ବିଜ୍ଞପ୍ତି ଓ ଆପ୍‌ଗୁଡ଼ିକ ବନ୍ଦ ଅଛି"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"ବନ୍ଦ କରନ୍ତୁ"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"ବନ୍ଦ ହୋଇଯାଇଛି"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"ବିଫଳ ହୋଇଛି: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-pa-rIN/strings.xml b/res/values-pa-rIN/strings.xml
deleted file mode 100644
index c077869..0000000
--- a/res/values-pa-rIN/strings.xml
+++ /dev/null
@@ -1,146 +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">
-    <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
-    <string name="folder_name" msgid="7371454440695724752"></string>
-    <string name="work_folder_name" msgid="3753320833950115786">"ਦਫ਼ਤਰ"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"ਐਪ ਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ ਹੋਇਆ ਹੈ।"</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"ਐਪ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
-    <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="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="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="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="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="permlab_install_shortcut" msgid="5632423390354674437">"ਸ਼ਾਰਟਕੱਟ ਸਥਾਪਤ ਕਰੋ"</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="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>
-    <string name="gadget_setup_text" msgid="8274003207686040488">"ਸਥਾਪਤ ਕਰੋ"</string>
-    <string name="uninstall_system_app_text" msgid="4172046090762920660">"ਇਹ ਇੱਕ ਸਿਸਟਮ ਐਪ ਹੈ ਅਤੇ ਇਸਨੂੰ ਅਣਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ।"</string>
-    <string name="folder_hint_text" msgid="6617836969016293992">"ਬਿਨਾਂ ਨਾਮ ਦਿੱਤਾ ਫੋਲਡਰ"</string>
-    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਨੂੰ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ਦੀ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ਸੂਚਨਾ</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ਦੀਆਂ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ਸੂਚਨਾਵਾਂ</item>
-    </plurals>
-    <string name="default_scroll_format" msgid="7475544710230993317">"ਸਫ਼ਾ %2$d ਦਾ %1$d"</string>
-    <string name="workspace_scroll_format" msgid="8458889198184077399">"ਹੋਮ ਸਕ੍ਰੀਨ %2$d ਦੀ %1$d"</string>
-    <string name="workspace_new_page" msgid="257366611030256142">"ਨਵਾਂ ਹੋਮ ਸਕ੍ਰੀਨ ਸਫ਼ਾ"</string>
-    <string name="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" msgid="6629239338071103179">"ਫੋਲਡਰ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"ਵਿਜੇਟ"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"ਵਾਲਪੇਪਰ"</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="icon_badging_title" msgid="874121399231955394">"ਸੂਚਨਾ ਬਿੰਦੂ"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"ਚਾਲੂ"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"ਬੰਦ"</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="icon_badging_service_title" msgid="2309733118428242174">"ਸੂਚਨਾ ਬਿੰਦੂ ਦਿਖਾਓ"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰਤੀਕ ਸ਼ਾਮਲ ਕਰੋ"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"ਨਵੀਆਂ ਐਪਾਂ ਲਈ"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"ਪ੍ਰਤੀਕ ਦੀ ਆਕ੍ਰਿਤੀ ਬਦਲੋ"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"ਸਿਸਟਮ ਦੀ ਪੂਰਵ-ਨਿਰਧਾਰਤ ਸੈਟਿੰਗ ਵਰਤੋ"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"ਵਰਗ"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"ਵਰਗਾਕਾਰ-ਚੱਕਰ"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"ਚੱਕਰ"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"ਹੰਝੂ ਦੀ ਬੂੰਦ"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"ਪ੍ਰਤੀਕ ਦੀ ਆਕ੍ਰਿਤੀ ਵਿੱਚ ਤਬਦੀਲੀਆਂ ਨੂੰ ਲਾਗੂ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</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_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="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="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="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>
-    <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_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> ਲਈ <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> ਸ਼ਾਰਟਕੱਟ"</string>
-    <string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> ਲਈ <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> ਸ਼ਾਰਟਕੱਟ ਅਤੇ <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> ਸੂਚਨਾਵਾਂ"</string>
-    <string name="action_dismiss_notification" msgid="5909461085055959187">"ਖਾਰਜ ਕਰੋ"</string>
-    <string name="notification_dismissed" msgid="6002233469409822874">"ਸੂਚਨਾ ਖਾਰਜ ਕੀਤੀ ਗਈ"</string>
-    <string name="all_apps_personal_tab" msgid="4190252696685155002">"ਨਿੱਜੀ"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"ਕਾਰਜ-ਸਥਾਨ"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ"</string>
-    <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"ਕਾਰਜ-ਸਥਾਨ ਐਪਾਂ ਇੱਥੇ ਲੱਭੋ"</string>
-    <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"ਹਰੇਕ ਕਾਰਜ-ਸਥਾਨ ਐਪ ਦਾ ਇੱਕ ਬੈਜ ਹੁੰਦਾ ਹੈ ਅਤੇ ਉਸਨੂੰ ਤੁਹਾਡੀ ਸੰਸਥਾ ਵੱਲੋਂ ਸੁਰੱਖਿਅਤ ਰੱਖਿਆ ਜਾਂਦਾ ਹੈ। ਵਧੇਰੇ ਆਸਾਨ ਪਹੁੰਚ ਲਈ ਐਪਾਂ ਨੂੰ ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਲਿਜਾਓ।"</string>
-    <string name="work_mode_on_label" msgid="4781128097185272916">"ਤੁਹਾਡੀ ਸੰਸਥਾ ਵੱਲੋਂ ਪ੍ਰਬੰਧਿਤ ਕੀਤਾ ਜਾਂਦਾ ਹੈ"</string>
-    <string name="work_mode_off_label" msgid="3194894777601421047">"ਸੂਚਨਾਵਾਂ ਅਤੇ ਐਪਾਂ ਬੰਦ ਹਨ"</string>
-    <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"ਬੰਦ ਕਰੋ"</string>
-    <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"ਬੰਦ ਕੀਤਾ ਗਿਆ"</string>
-</resources>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 3400e7b..d4cd5be 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,9 +65,9 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"ਇਹ ਇੱਕ ਸਿਸਟਮ ਐਪ ਹੈ ਅਤੇ ਇਸਨੂੰ ਅਣਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ।"</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"ਬਿਨਾਂ ਨਾਮ ਦਿੱਤਾ ਫੋਲਡਰ"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਨੂੰ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ਦੀ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ਸੂਚਨਾ</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ਦੀਆਂ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ਸੂਚਨਾਵਾਂ</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ਦੀ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ਸੂਚਨਾ</item>
+      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ਦੀਆਂ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ਸੂਚਨਾਵਾਂ</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"ਸਫ਼ਾ %2$d ਦਾ %1$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"ਹੋਮ ਸਕ੍ਰੀਨ %2$d ਦੀ %1$d"</string>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"ਫੋਲਡਰ: <xliff:g id="NAME">%1$s</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="icon_badging_title" msgid="874121399231955394">"ਸੂਚਨਾ ਬਿੰਦੂ"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"ਚਾਲੂ"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"ਬੰਦ"</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="icon_badging_service_title" msgid="2309733118428242174">"ਸੂਚਨਾ ਬਿੰਦੂ ਦਿਖਾਓ"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"ਸੂਚਨਾ ਬਿੰਦੂ ਦਿਖਾਓ"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰਤੀਕ ਸ਼ਾਮਲ ਕਰੋ"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"ਨਵੀਆਂ ਐਪਾਂ ਲਈ"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"ਪ੍ਰਤੀਕ ਦੀ ਆਕ੍ਰਿਤੀ ਬਦਲੋ"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"ਸਿਸਟਮ ਦੀ ਪੂਰਵ-ਨਿਰਧਾਰਤ ਸੈਟਿੰਗ ਵਰਤੋ"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"ਵਰਗ"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"ਵਰਗਾਕਾਰ-ਚੱਕਰ"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"ਚੱਕਰ"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"ਹੰਝੂ ਦੀ ਬੂੰਦ"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"ਪ੍ਰਤੀਕ ਦੀ ਆਕ੍ਰਿਤੀ ਵਿੱਚ ਤਬਦੀਲੀਆਂ ਨੂੰ ਲਾਗੂ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"ਅਗਿਆਤ"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ਹਟਾਓ"</string>
     <string name="abandoned_search" msgid="891119232568284442">"ਖੋਜੋ"</string>
@@ -108,14 +102,13 @@
     <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>
-    <!-- no translation found for widgets_list (796804551140113767) -->
-    <skip />
-    <!-- no translation found for widgets_list_closed (6141506579418771922) -->
-    <skip />
-    <string name="action_add_to_workspace" msgid="8902165848117513641">"ਹੋਮ ਸਕ੍ਰੀਨ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰੋ"</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="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>
@@ -146,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"ਸੂਚਨਾਵਾਂ ਅਤੇ ਐਪਾਂ ਬੰਦ ਹਨ"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"ਬੰਦ ਕਰੋ"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"ਬੰਦ ਕੀਤਾ ਗਿਆ"</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 776f39d..6885960 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,7 +65,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"To aplikacja systemowa i nie można jej odinstalować."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Folder bez nazwy"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> jest wyłączona"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="few"><xliff:g id="APP_NAME_2">%1$s</xliff:g> – <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> powiadomienia</item>
       <item quantity="many"><xliff:g id="APP_NAME_2">%1$s</xliff:g> – <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> powiadomień</item>
       <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> – <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> powiadomienia</item>
@@ -81,27 +82,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</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 strony głównej"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Funkcja wyłączona przez administratora"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Zezwalaj na obrót ekranu głównego"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Po obróceniu telefonu"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Plakietki z powiadomieniami"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Włączono"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Wyłączono"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Plakietki z powiadomieniami"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Włączono"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Wyłączono"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Wymagany jest dostęp do powiadomień"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Aby pokazać plakietki z powiadomieniami, włącz powiadomienia aplikacji <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Zmień ustawienia"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Pokaż plakietki z powiadomieniami"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Pokaż plakietki z powiadomieniami"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Dodaj ikonę do ekranu głównego"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"W przypadku nowych aplikacji"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Zmień kształt ikon"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"na ekranie głównym"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Użyj ustawienia domyślnego"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Kwadrat"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Zaokrąglony kwadrat"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Okrąg"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Łza"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Zmieniam kształt ikon"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Brak informacji"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Usuń"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Szukaj"</string>
@@ -112,10 +106,11 @@
     <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 strony głównej"</string>
+    <string name="action_add_to_workspace" msgid="8902165848117513641">"Dodaj do ekranu głównego"</string>
     <string name="action_move_here" msgid="2170188780612570250">"Przenieś element tutaj"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Element został dodany do ekranu głównego"</string>
     <string name="item_removed" msgid="851119963877842327">"Element został usunięty"</string>
+    <string name="undo" msgid="4151576204245173321">"Cofnij"</string>
     <string name="action_move" msgid="4339390619886385032">"Przenieś element"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Przenieś do wiersza <xliff:g id="NUMBER_0">%1$s</xliff:g> w kolumnie <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Przenieś do pozycji <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
@@ -146,4 +141,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Powiadomienia i aplikacje są wyłączone"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Zamknij"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Zamknięto"</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 4010591..071b71e 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -39,6 +39,7 @@
     <string name="all_apps_loading_message" msgid="5813968043155271636">"A carregar aplicações…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"Nenhuma aplicação 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>
@@ -64,9 +65,9 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"É uma aplicação de sistema e não pode ser desinstalada."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Pasta sem nome"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> desativado"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, tem <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificações.</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, tem <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notificação.</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="other">A aplicação <xliff:g id="APP_NAME_2">%1$s</xliff:g> tem <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificações.</item>
+      <item quantity="one">A aplicação <xliff:g id="APP_NAME_0">%1$s</xliff:g> tem <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notificação</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"Página %1$d de %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Ecrã principal %1$d de %2$d"</string>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Pasta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Imagens de fundo"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Definições da página inicial"</string>
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Estilos e imagens de 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>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Quando o telemóvel é rodado"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Pontos de notificação"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Ativada"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Desativada"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Pontos de notificação"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Ativados"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Desativados"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Acesso a notificações necessário"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Para mostrar os Pontos de notificação, ative as notificações de aplicações para o <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Alterar definições"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Mostrar pontos de notificação"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Mostrar pontos de notificação"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Adicionar ícone ao ecrã principal"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Para novas aplicações"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Alterar forma do ícone"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"no ecrã principal"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Utilizar a predefinição do sistema"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Quadrado"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Quadrado e círculo"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Círculo"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Lágrima"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"A aplicar alterações à forma do ícone..."</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>
@@ -110,10 +104,11 @@
     <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>
+    <string name="action_add_to_workspace" msgid="8902165848117513641">"Adicionar ao ecrã principal"</string>
     <string name="action_move_here" msgid="2170188780612570250">"Mover o item para aqui"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Item adicionado ao ecrã principal"</string>
     <string name="item_removed" msgid="851119963877842327">"Item removido"</string>
+    <string name="undo" msgid="4151576204245173321">"Anular"</string>
     <string name="action_move" msgid="4339390619886385032">"Mover item"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Mover para a linha <xliff:g id="NUMBER_0">%1$s</xliff:g>, coluna <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Mover para a posição <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"As notificações e as aplicações estão desativadas."</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Fechar"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Fechado"</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 6238e7a..e88ff18 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -33,12 +33,13 @@
     <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="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 posicionar manualmente"</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>
     <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>
@@ -64,9 +65,9 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Este é um app do sistema e não pode ser desinstalado."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Pasta sem nome"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> desativado"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="one">O app <xliff:g id="APP_NAME_2">%1$s</xliff:g> tem <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificação</item>
-      <item quantity="other">O app <xliff:g id="APP_NAME_2">%1$s</xliff:g> tem <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificações</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="one">O app <xliff:g id="APP_NAME_2">%1$s</xliff:g>tem <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificação</item>
+      <item quantity="other">O app <xliff:g id="APP_NAME_2">%1$s</xliff:g>tem <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificações</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"Página %1$d de %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Tela inicial %1$d de %2$d"</string>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Pasta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Planos de fundo"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Configurações da tela inicial"</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="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>
-    <string name="icon_badging_title" msgid="874121399231955394">"Pontos de notificação"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Ativado"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Desativado"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Pontos de notificação"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Ativado"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Desativado"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Acesso a notificações necessário"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Para mostrar pontos de notificação, ative as notificações de app para <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Alterar configurações"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Mostrar pontos de notificação"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Mostrar pontos de notificação"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Adicionar ícone à tela inicial"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Para novos apps"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Alterar forma de ícones"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"na tela inicial"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Usar padrão do sistema"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Quadrado"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Quadrado arredondado"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Círculo"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Lágrima"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Aplicando alterações na forma dos ícones"</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>
@@ -114,6 +108,7 @@
     <string name="action_move_here" msgid="2170188780612570250">"Mover item para cá"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Item adicionado à tela inicial"</string>
     <string name="item_removed" msgid="851119963877842327">"Item removido"</string>
+    <string name="undo" msgid="4151576204245173321">"Desfazer"</string>
     <string name="action_move" msgid="4339390619886385032">"Mover item"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Mover para a linha <xliff:g id="NUMBER_0">%1$s</xliff:g>, coluna <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Mover para a posição <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"As notificações e os apps estão desativados"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Fechar"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Fechado"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"Falha: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index fb5fb96..1251d7e 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -50,7 +51,7 @@
     <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 aplicație"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Informații despre aplicații"</string>
     <string name="install_drop_target_label" msgid="2539096853673231757">"Instalați"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalează comenzi rapide"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite unei aplicații să adauge comenzi rapide fără intervenția utilizatorului."</string>
@@ -64,9 +65,9 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Aceasta este o aplicație de sistem și nu poate fi dezinstalată."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Dosar fără nume"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"S-a dezactivat <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="few"><xliff:g id="APP_NAME_2">%1$s</xliff:g> are <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificări</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> are <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> de notificări</item>
+      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>​ are <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> de notificări</item>
       <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> are <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notificare</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"Pagina %1$d din %2$d"</string>
@@ -80,27 +81,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Dosar: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgeturi"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Imagini de fundal"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Setări pentru ecranul de pornire"</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>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Dezactivată de administrator"</string>
-    <string name="allow_rotation_title" msgid="7728578836261442095">"Permiteți rotirea ecranului de pornire"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Permite rotirea ecranului de pornire"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Când telefonul este rotit"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Puncte de notificare"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Activat"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Dezactivat"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Puncte de notificare"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Activate"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Dezactivate"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Este necesar accesul la notificări"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Pentru a afișa punctele de notificare, activați notificările din aplicație pentru <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Modificați setările"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Afișați punctele de notificare"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Afișați punctele de notificare"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Adaugă pictograme în ecranul de pornire"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Pentru aplicații noi"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Schimbați forma pictogramei"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"pe ecranul de pornire"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Folosiți setarea prestabilită a sistemului"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Pătrat"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Pătrat cu colțuri rotunjite"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Cerc"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Lacrimă"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Se aplică modificările aduse formei pictogramei"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Necunoscut"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Eliminați"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Căutați"</string>
@@ -115,6 +109,7 @@
     <string name="action_move_here" msgid="2170188780612570250">"Mutați elementul aici"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Element adăugat pe ecranul de pornire"</string>
     <string name="item_removed" msgid="851119963877842327">"Element eliminat"</string>
+    <string name="undo" msgid="4151576204245173321">"Anulați"</string>
     <string name="action_move" msgid="4339390619886385032">"Mutați elementul"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Mutați pe rândul <xliff:g id="NUMBER_0">%1$s</xliff:g>, coloana <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Mutați pe poziția <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
@@ -145,4 +140,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Notificările și aplicațiile sunt dezactivate"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Închideți"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Închis"</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 dd70521..34e267d 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,11 +65,11 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Это системное приложение, его нельзя удалить."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Папка без названия"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Приложение <xliff:g id="APP_NAME">%1$s</xliff:g> отключено"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="one">В приложении \"<xliff:g id="APP_NAME_2">%1$s</xliff:g>\" <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> уведомление</item>
-      <item quantity="few">В приложении \"<xliff:g id="APP_NAME_2">%1$s</xliff:g>\" <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> уведомления</item>
-      <item quantity="many">В приложении \"<xliff:g id="APP_NAME_2">%1$s</xliff:g>\" <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> уведомлений</item>
-      <item quantity="other">В приложении \"<xliff:g id="APP_NAME_2">%1$s</xliff:g>\" <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> уведомления</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="one">В приложении \"<xliff:g id="APP_NAME_2">%1$s</xliff:g>\" <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> уведомление</item>
+      <item quantity="few">В приложении \"<xliff:g id="APP_NAME_2">%1$s</xliff:g>\" <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> уведомления</item>
+      <item quantity="many">В приложении \"<xliff:g id="APP_NAME_2">%1$s</xliff:g>\" <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> уведомлений</item>
+      <item quantity="other">В приложении \"<xliff:g id="APP_NAME_2">%1$s</xliff:g>\" <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> уведомления</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"Стр. %1$d из %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Главный экран %1$d из %2$d"</string>
@@ -81,29 +82,22 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Папка: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Виджеты"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Обои"</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>
-    <string name="icon_badging_title" msgid="874121399231955394">"Значки уведомлений"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Вкл."</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Выкл."</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="icon_badging_service_title" msgid="2309733118428242174">"Показывать значки уведомлений"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Показывать значки уведомлений"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Добавлять значки"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Добавлять значки установленных приложений на главный экран"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Изменить форму значков"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"на главном экране"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Использовать системные настройки по умолчанию"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Квадрат"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Квадрат с закругленными краями"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Круг"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Капля"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Применение изменений…"</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>
@@ -116,6 +110,7 @@
     <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>
@@ -146,4 +141,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Уведомления и приложения отключены."</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Закрыть"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Закрыта"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"Не удалось выполнить действие (<xliff:g id="WHAT">%1$s</xliff:g>)."</string>
 </resources>
diff --git a/res/values-si-rLK/strings.xml b/res/values-si-rLK/strings.xml
deleted file mode 100644
index 10835dc..0000000
--- a/res/values-si-rLK/strings.xml
+++ /dev/null
@@ -1,146 +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">
-    <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
-    <string name="folder_name" msgid="7371454440695724752"></string>
-    <string name="work_folder_name" msgid="3753320833950115786">"කාර්යාලය"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"යෙදුම ස්ථාපනය කර නැත."</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"යෙදුම නොතිබේ"</string>
-    <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="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="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="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="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="permlab_install_shortcut" msgid="5632423390354674437">"කෙටිමං ස්ථාපනය කරන්න"</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="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>
-    <string name="gadget_setup_text" msgid="8274003207686040488">"ස්ථාපනය කරන්න"</string>
-    <string name="uninstall_system_app_text" msgid="4172046090762920660">"මෙය පද්ධති යෙදුමක් වන අතර අස්ථාපනය කළ නොහැක."</string>
-    <string name="folder_hint_text" msgid="6617836969016293992">"නම් නොකළ ෆෝල්ඩරය"</string>
-    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> අබල කෙරිණි"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, දැනුම්දීම් <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>ක් ඇත</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, දැනුම්දීම් <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>ක් ඇත</item>
-    </plurals>
-    <string name="default_scroll_format" msgid="7475544710230993317">"%2$d හි %1$d පිටුව"</string>
-    <string name="workspace_scroll_format" msgid="8458889198184077399">"මුල් පිටු තිරය %2$d හි %1$d"</string>
-    <string name="workspace_new_page" msgid="257366611030256142">"නව මුල් පිටුව"</string>
-    <string name="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" msgid="6629239338071103179">"ෆෝල්ඩරය: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"විජට්"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"වෝල්පේපර"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Home සැකසීම්"</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="icon_badging_title" msgid="874121399231955394">"දැනුම්දීම් තිත්"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"ක්‍රියාත්මකයි"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"ක්‍රියාවිරහිතයි"</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="icon_badging_service_title" msgid="2309733118428242174">"දැනුම් දීමේ තිත් පෙන්වන්න"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"මුල් පිටු තිරය වෙත අයිකනය එක් කරන්න"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"නව යෙදුම් සඳහා"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"නිරූපක හැඩය වෙනස් කරන්න"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"මුල් පිටු තිරය මත"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"පද්ධති පෙරනිමි භාවිත කරන්න"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"සමචතුරස්‍රය"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"හතරැස් කවය"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"කවය"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"කඳුළු බිංදුව"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"නිරූපක හැඩය වෙනස් කිරීම් යොදමින්"</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_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="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="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="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>
-    <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_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> සඳහා කෙටි මං <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>"</string>
-    <string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> සඳහා කෙටි මං <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>ක් සහ දැනුම්දීම් <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g>ක්"</string>
-    <string name="action_dismiss_notification" msgid="5909461085055959187">"ඉවතලන්න"</string>
-    <string name="notification_dismissed" msgid="6002233469409822874">"දැනුම්දීම ඉවතලන ලදී"</string>
-    <string name="all_apps_personal_tab" msgid="4190252696685155002">"පුද්ගලික"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"කාර්යාලය"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"කාර්යාල පැතිකඩ"</string>
-    <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"මෙහි කාර්යාල යෙදුම් සොයා ගන්න"</string>
-    <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"සෑම කාර්යාල යෙදුමකම ලාංඡනයක් ඇත ඇති අතර එය ඔබේ සංවිධානය මගින් සුරක්ෂිතව තබා ගනී. වඩාත් පහසු ප්‍රවේශයකට යෙදුම් ඔබේ මුල් පිටු තිරය වෙත ගෙන යන්න."</string>
-    <string name="work_mode_on_label" msgid="4781128097185272916">"ඔබේ සංවිධානය විසින් කළමනාකරණය කරනු ලැබේ"</string>
-    <string name="work_mode_off_label" msgid="3194894777601421047">"දැනුම්දීම් සහ යෙදුම් ක්‍රියාවිරහිතයි"</string>
-    <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"වසන්න"</string>
-    <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"වසා ඇත"</string>
-</resources>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index ee61124..ef99a59 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,7 +65,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"මෙය පද්ධති යෙදුමක් වන අතර අස්ථාපනය කළ නොහැක."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"නම් නොකළ ෆෝල්ඩරය"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> අබල කෙරිණි"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, දැනුම්දීම් <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>ක් ඇත</item>
       <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, දැනුම්දීම් <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>ක් ඇත</item>
     </plurals>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"ෆෝල්ඩරය: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"විජට්"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"වෝල්පේපර"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Home සැකසීම්"</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="icon_badging_title" msgid="874121399231955394">"දැනුම්දීම් තිත්"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"ක්‍රියාත්මකයි"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"ක්‍රියාවිරහිතයි"</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="icon_badging_service_title" msgid="2309733118428242174">"දැනුම් දීමේ තිත් පෙන්වන්න"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"දැනුම්දීම් තිත් පෙන්වන්න"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"මුල් පිටු තිරය වෙත අයිකනය එක් කරන්න"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"නව යෙදුම් සඳහා"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"නිරූපක හැඩය වෙනස් කරන්න"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"මුල් පිටු තිරය මත"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"පද්ධති පෙරනිමි භාවිත කරන්න"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"සමචතුරස්‍රය"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"හතරැස් කවය"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"කවය"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"කඳුළු බිංදුව"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"නිරූපක හැඩය වෙනස් කිරීම් යොදමින්"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"නොදනී"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ඉවත් කරන්න"</string>
     <string name="abandoned_search" msgid="891119232568284442">"සොයන්න"</string>
@@ -114,6 +108,7 @@
     <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>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"දැනුම්දීම් සහ යෙදුම් ක්‍රියාවිරහිතයි"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"වසන්න"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"වසා ඇත"</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 649aecc..340a128 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -33,12 +33,13 @@
     <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="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">"Položku umiestnite ručne klepnutím a podržaním"</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>
     <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>
@@ -64,11 +65,11 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Toto je systémová aplikácia a nedá sa odinštalovať."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Nepomenovaný priečinok"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> je deaktivovaná"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="few">Aplikácia <xliff:g id="APP_NAME_2">%1$s</xliff:g> má <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> upozornenia</item>
-      <item quantity="many">Aplikácia <xliff:g id="APP_NAME_2">%1$s</xliff:g> má <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> upozornenia</item>
-      <item quantity="other">Aplikácia <xliff:g id="APP_NAME_2">%1$s</xliff:g> má <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> upozornení</item>
-      <item quantity="one">Aplikácia <xliff:g id="APP_NAME_0">%1$s</xliff:g> má <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> upozornenie</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="few"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, má <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> upozornenia</item>
+      <item quantity="many"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, has <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifications</item>
+      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, má <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> upozornení</item>
+      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, má <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> upozornenie</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"Stránka %1$d z %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Plocha %1$d z %2$d"</string>
@@ -81,27 +82,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Priečinok: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Miniaplikácie"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Tapety"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Nastavenia Home"</string>
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Štýly a tapety"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Nastavenia plochy"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Zakázané vaším správcom"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Povoliť otáčanie plochy"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Pri otočení telefónu"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Bodky upozornení"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Zapnuté"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Vypnuté"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Bodky upozornení"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Zapnuté"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Vypnuté"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Vyžaduje sa prístup k upozorneniam"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Ak chcete, aby sa zobrazovali bodky upozornení, zapnite upozornenia aplikácie <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Zmeniť nastavenia"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Zobrazovať bodky upozornení"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Zobrazovať bodky upozornení"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Pridať ikonu na plochu"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Pri inštalácii novej aplikácie"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Zmeniť tvar ikony"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"na ploche"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Použiť predvolené nastavenie systému"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Štvorec"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Okrúhly štvorec"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Kruh"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Slza"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Tvar ikony sa mení"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Neznáme"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Odstrániť"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Vyhľadať"</string>
@@ -116,6 +110,7 @@
     <string name="action_move_here" msgid="2170188780612570250">"Presunúť položku sem"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Položka bola pridaná na plochu"</string>
     <string name="item_removed" msgid="851119963877842327">"Položka bola odstránená"</string>
+    <string name="undo" msgid="4151576204245173321">"Späť"</string>
     <string name="action_move" msgid="4339390619886385032">"Presunúť položku"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Presunúť do stĺpca <xliff:g id="NUMBER_1">%2$s</xliff:g> v riadku <xliff:g id="NUMBER_0">%1$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Presunúť na <xliff:g id="NUMBER">%1$s</xliff:g>. miesto"</string>
@@ -146,4 +141,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Upozornenia a aplikácie sú vypnuté"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Zavrieť"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Zavreté"</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 aa3bc4f..044a4b4 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -49,7 +50,7 @@
     <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">"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>
     <string name="install_drop_target_label" msgid="2539096853673231757">"Namesti"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"namestitev bližnjic"</string>
@@ -64,11 +65,11 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"To je sistemska aplikacija in je ni mogoče odstraniti."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Neimenovana mapa"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je onemogočena"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="one">Aplikacija <xliff:g id="APP_NAME_2">%1$s</xliff:g> ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obvestilo</item>
-      <item quantity="two">Aplikacija <xliff:g id="APP_NAME_2">%1$s</xliff:g> ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obvestili</item>
-      <item quantity="few">Aplikacija <xliff:g id="APP_NAME_2">%1$s</xliff:g> ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obvestila</item>
-      <item quantity="other">Aplikacija <xliff:g id="APP_NAME_2">%1$s</xliff:g> ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obvestil</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obvestilo</item>
+      <item quantity="two"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obvestili</item>
+      <item quantity="few"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obvestila</item>
+      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obvestil</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"Stran %1$d od %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Začetni zaslon %1$d od %2$d"</string>
@@ -81,27 +82,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Mapa: <xliff:g id="NAME">%1$s</xliff:g>"</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">"Nastavitve začetnega zaslona"</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_desc" msgid="8662546029078692509">"Ko se telefon zasuka"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Obvestilne pike"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Vklopljeno"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Izklopljeno"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Obvestilne pike"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Vklopljeno"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Izklopljeno"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Potreben je dostop do obvestil"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Za prikaz obvestilnih pik vklopite obvestila aplikacije <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Spremeni nastavitve"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Pokaži obvestilne pike"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Pokaži obvestilne pike"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Dodaj ikono na začetni zaslon"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Za nove aplikacije"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Spremeni obliko ikon"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"na začetnem zaslonu"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Uporabi privzeto nastavitev sistema"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Zaobljen kvadrat"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Krog"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Solza"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Uveljavljanje spremenjene oblike ikon"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Neznano"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Odstrani"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Iskanje"</string>
@@ -116,6 +110,7 @@
     <string name="action_move_here" msgid="2170188780612570250">"Premik elementa sem"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Element je bil dodan na začetni zaslon"</string>
     <string name="item_removed" msgid="851119963877842327">"Element je bil odstranjen"</string>
+    <string name="undo" msgid="4151576204245173321">"Razveljavi"</string>
     <string name="action_move" msgid="4339390619886385032">"Premik elementa"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Premik v <xliff:g id="NUMBER_0">%1$s</xliff:g>. vrstico <xliff:g id="NUMBER_1">%2$s</xliff:g>. stolpca"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Premk na mesto št. <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
@@ -146,4 +141,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Obvestila in aplikacije – izklopljeno"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Zapri"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Zaprto"</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-rAL/strings.xml b/res/values-sq-rAL/strings.xml
deleted file mode 100644
index 82cc42a..0000000
--- a/res/values-sq-rAL/strings.xml
+++ /dev/null
@@ -1,146 +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">
-    <string name="app_name" msgid="649227358658669779">"Nisësi3"</string>
-    <string name="folder_name" msgid="7371454440695724752"></string>
-    <string name="work_folder_name" msgid="3753320833950115786">"Puna"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"Aplikacioni nuk është i instaluar."</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"Aplikacioni nuk mundësohet"</string>
-    <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="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>
-    <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="notifications_header" msgid="1404149926117359025">"Njoftimet"</string>
-    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Prek dhe mbaj prekur për të zgjedhur një shkurtore."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Prek dy herë dhe mbaj prekur për të zgjedhur një shkurtore 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">"Informacion mbi aplikacionin"</string>
-    <string name="install_drop_target_label" msgid="2539096853673231757">"Instalo"</string>
-    <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalo shkurtore"</string>
-    <string name="permdesc_install_shortcut" msgid="923466509822011139">"Lejon një aplikacion të shtojë shkurtore pa ndërhyrjen e përdoruesit."</string>
-    <string name="permlab_read_settings" msgid="1941457408239617576">"lexo cilësimet dhe shkurtoret e ekranit bazë"</string>
-    <string name="permdesc_read_settings" msgid="5833423719057558387">"Lejon aplikacionin të lexojë cilësimet dhe shkurtoret në ekranin bazë."</string>
-    <string name="permlab_write_settings" msgid="3574213698004620587">"shkruaj cilësimet dhe shkurtoret e ekranit bazë"</string>
-    <string name="permdesc_write_settings" msgid="5440712911516509985">"Lejon aplikacionin të ndryshojë cilësimet dhe shkurtoret në ekranin bazë."</string>
-    <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> nuk lejohet të kryejë telefonata"</string>
-    <string name="gadget_error_text" msgid="6081085226050792095">"Problem në ngarkimin e miniaplikacionit"</string>
-    <string name="gadget_setup_text" msgid="8274003207686040488">"Konfiguro"</string>
-    <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ky është aplikacion sistemi dhe nuk mund të çinstalohet."</string>
-    <string name="folder_hint_text" msgid="6617836969016293992">"Dosje e paemërtuar"</string>
-    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> u çaktivizua"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ka <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> njoftime</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, ka <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> njoftime</item>
-    </plurals>
-    <string name="default_scroll_format" msgid="7475544710230993317">"Faqja: %1$d nga gjithsej %2$d"</string>
-    <string name="workspace_scroll_format" msgid="8458889198184077399">"Ekrani bazë: %1$d nga gjithsej %2$d"</string>
-    <string name="workspace_new_page" msgid="257366611030256142">"Faqja e ekranit të ri kryesor"</string>
-    <string name="folder_opened" msgid="94695026776264709">"Dosja u hap, <xliff:g id="WIDTH">%1$d</xliff:g> me <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="4625795376335528256">"Trokit për të mbyllur dosjen"</string>
-    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Trokit për të ruajtur riemërtimin"</string>
-    <string name="folder_closed" msgid="4100806530910930934">"Dosja u mbyll"</string>
-    <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" msgid="6629239338071103179">"Dosja: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Miniaplikacionet"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"Imazhet e sfondit"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Cilësimet e Home"</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_desc" msgid="8662546029078692509">"Kur telefoni rrotullohet"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Pikat e njoftimeve"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Aktiv"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Joaktiv"</string>
-    <string name="title_missing_notification_access" msgid="7503287056163941064">"Nevojitet qasja në njoftime"</string>
-    <string name="msg_missing_notification_access" msgid="281113995110910548">"Për të shfaqur \"Pikat e njoftimeve\", aktivizo njoftimet e aplikacionit për <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="title_change_settings" msgid="1376365968844349552">"Ndrysho cilësimet"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Shfaq pikat e njoftimeve"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Shto ikonë në ekranin bazë"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Për aplikacionet e reja"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Ndrysho formën e ikonës"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"në ekranin bazë"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Përdor parazgjedhjen e sistemit"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Katror"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Katror me kënde të rrumbullakëta"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Rreth"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Pikë loti"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Po zbatohen ndryshimet e formës së ikonës"</string>
-    <string name="package_state_unknown" msgid="7592128424511031410">"I panjohur"</string>
-    <string name="abandoned_clean_this" msgid="7610119707847920412">"Hiq"</string>
-    <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_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="action_add_to_workspace" msgid="8902165848117513641">"Shto në Ekranin bazë"</string>
-    <string name="action_move_here" msgid="2170188780612570250">"Zhvendose artikullin këtu"</string>
-    <string name="item_added_to_workspace" msgid="4211073925752213539">"Artikulli u shtua tek ekrani bazë"</string>
-    <string name="item_removed" msgid="851119963877842327">"Artikulli u hoq"</string>
-    <string name="action_move" msgid="4339390619886385032">"Zhvendose artikullin"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Zhvendos te rreshti <xliff:g id="NUMBER_0">%1$s</xliff:g>, kolona <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
-    <string name="move_to_position" msgid="6750008980455459790">"Zhvendos te pozicioni <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
-    <string name="move_to_hotseat_position" msgid="6295412897075147808">"Zhvendos te pozicioni <xliff:g id="NUMBER">%1$s</xliff:g> i preferencave"</string>
-    <string name="item_moved" msgid="4606538322571412879">"Artikulli u zhvendos"</string>
-    <string name="add_to_folder" msgid="9040534766770853243">"Shto te dosja: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="add_to_folder_with_app" msgid="4534929978967147231">"Shto te dosja me <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="added_to_folder" msgid="4793259502305558003">"Artikulli u shtua te dosja"</string>
-    <string name="create_folder_with" msgid="4050141361160214248">"Krijo një dosje me: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="folder_created" msgid="6409794597405184510">"Dosja u krijua"</string>
-    <string name="action_move_to_workspace" msgid="1603837886334246317">"Zhvendose në Ekranin bazë"</string>
-    <string name="action_resize" msgid="1802976324781771067">"Ndrysho madhësinë"</string>
-    <string name="action_increase_width" msgid="8773715375078513326">"Rrit gjerësinë"</string>
-    <string name="action_increase_height" msgid="459390020612501122">"Rrit lartësinë"</string>
-    <string name="action_decrease_width" msgid="1374549771083094654">"Zvogëlo gjerësinë"</string>
-    <string name="action_decrease_height" msgid="282377193880900022">"Zvogëlo lartësinë"</string>
-    <string name="widget_resized" msgid="9130327887929620">"Madhësia e miniaplikacionit u ndryshua me gjerësinë <xliff:g id="NUMBER_0">%1$s</xliff:g> dhe lartësinë <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
-    <string name="action_deep_shortcut" msgid="2864038805849372848">"Shkurtoret"</string>
-    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> shkurtesa për <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
-    <string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> shkurtore dhe <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> njoftime për <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
-    <string name="action_dismiss_notification" msgid="5909461085055959187">"Hiqe"</string>
-    <string name="notification_dismissed" msgid="6002233469409822874">"Njoftimi u hoq"</string>
-    <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personale"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"Punë"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"Profili i punës"</string>
-    <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Gjej këtu aplikacionet e punës"</string>
-    <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Secili aplikacion pune ka një distinktiv dhe mbahet i sigurt nga organizata jote. Zhvendosi aplikacionet e punës në ekranin tënd kryesor për qasje më të lehtë."</string>
-    <string name="work_mode_on_label" msgid="4781128097185272916">"Menaxhohet nga organizata jote"</string>
-    <string name="work_mode_off_label" msgid="3194894777601421047">"Njoftimet dhe aplikacionet janë joaktive"</string>
-    <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Mbyll"</string>
-    <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Mbyllur"</string>
-</resources>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 8cf0a81..48ae26e 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -39,6 +39,7 @@
     <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 prekur për të zgjedhur një shkurtore."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Prek dy herë dhe mbaj prekur për të zgjedhur një shkurtore ose për të përdorur veprimet e personalizuara."</string>
@@ -64,9 +65,9 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ky është aplikacion sistemi dhe nuk mund të çinstalohet."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Dosje e paemërtuar"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> u çaktivizua"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ka <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> njoftime</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, ka <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> njoftime</item>
+      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, ka <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> njoftim</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"Faqja: %1$d nga gjithsej %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Ekrani bazë: %1$d nga gjithsej %2$d"</string>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Dosja: <xliff:g id="NAME">%1$s</xliff:g>"</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 Home"</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_desc" msgid="8662546029078692509">"Kur telefoni rrotullohet"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Pikat e njoftimeve"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Aktiv"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Joaktiv"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Pikat e njoftimeve"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Aktiv"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Joaktiv"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Nevojitet qasja në njoftime"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Për të shfaqur \"Pikat e njoftimeve\", aktivizo njoftimet e aplikacionit për <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Ndrysho cilësimet"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Shfaq pikat e njoftimeve"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Shfaq pikat e njoftimeve"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Shto ikonë në ekranin bazë"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Për aplikacionet e reja"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Ndrysho formën e ikonës"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"në ekranin bazë"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Përdor parazgjedhjen e sistemit"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Katror"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Katror me kënde të rrumbullakëta"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Rreth"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Pikë loti"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Po zbatohen ndryshimet e formës së ikonës"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"I panjohur"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Hiq"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Kërko"</string>
@@ -114,6 +108,7 @@
     <string name="action_move_here" msgid="2170188780612570250">"Zhvendose artikullin këtu"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Artikulli u shtua tek ekrani bazë"</string>
     <string name="item_removed" msgid="851119963877842327">"Artikulli u hoq"</string>
+    <string name="undo" msgid="4151576204245173321">"Zhbëj"</string>
     <string name="action_move" msgid="4339390619886385032">"Zhvendose artikullin"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Zhvendos te rreshti <xliff:g id="NUMBER_0">%1$s</xliff:g>, kolona <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Zhvendos te pozicioni <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Njoftimet dhe aplikacionet janë joaktive"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Mbyll"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Mbyllur"</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 42522a5..4e52592 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,10 +65,10 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ово је системска апликација и не може да се деинсталира."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Неименовани директоријум"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Апликација <xliff:g id="APP_NAME">%1$s</xliff:g> је онемогућена"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> има <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> обавештење</item>
-      <item quantity="few"><xliff:g id="APP_NAME_2">%1$s</xliff:g> има <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> обавештења</item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> има <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> обавештења</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, има <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> обавештење</item>
+      <item quantity="few"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, има <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> обавештења</item>
+      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, има <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> обавештења</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"%1$d. страница од %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%1$d. почетни екран од %2$d"</string>
@@ -80,27 +81,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Директоријум: <xliff:g id="NAME">%1$s</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="icon_badging_title" msgid="874121399231955394">"Тачке за обавештења"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Укључено"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Искључено"</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="icon_badging_service_title" msgid="2309733118428242174">"Приказуј тачке за обавештења"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Приказуј тачке за обавештења"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Додај икону на почетни екран"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"За нове апликације"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Промените облик икона"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"на почетном екрану"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Користи подразумевано системско подешавање"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Квадрат"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Заобљени квадрат"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Круг"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Суза"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Примењују се промене облика икона"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Непознато"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Уклони"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Претражи"</string>
@@ -111,10 +105,11 @@
     <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="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>
@@ -145,4 +140,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Обавештења и апликације су искључени"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Затвори"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Затворено"</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 957a4cb..e7400a6 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,7 +65,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Det här är en systemapp som inte kan avinstalleras."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Namnlös mapp"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> har inaktiverats"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> har <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> aviseringar</item>
       <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> har <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> avisering</item>
     </plurals>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Mapp: <xliff:g id="NAME">%1$s</xliff:g>"</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>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Inaktiverat av administratören"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Tillåt rotering av startskärmen"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"När mobilen vrids"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Aviseringsprickar"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"På"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Av"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Aviseringsprickar"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"På"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Av"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Åtkomst till aviseringar krävs"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Aktivera appaviseringar för <xliff:g id="NAME">%1$s</xliff:g> om du vill att aviseringsprickar ska visas"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Ändra inställningar"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Visa aviseringsprickar"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Visa aviseringsprickar"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Lägg till ikonen på startskärmen"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"För nya appar"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Ändra form på ikoner"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"på startskärmen"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Använd systemstandard"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Kvirkel"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Cirkel"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Droppe"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Ikonernas form ändras"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Okänt"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Ta bort"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Sök"</string>
@@ -114,6 +108,7 @@
     <string name="action_move_here" msgid="2170188780612570250">"Flytta objekt hit"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Objektet har lagts till på startskärmen"</string>
     <string name="item_removed" msgid="851119963877842327">"Objektet har tagits bort"</string>
+    <string name="undo" msgid="4151576204245173321">"Ångra"</string>
     <string name="action_move" msgid="4339390619886385032">"Flytta objekt"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Flytta till rad <xliff:g id="NUMBER_0">%1$s</xliff:g>, kolumn <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Flytta till plats <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Aviseringar och appar är inaktiverade"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Stäng"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Stängd"</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 faa40a7..6f33c4f 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -49,7 +50,7 @@
     <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">"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>
     <string name="install_drop_target_label" msgid="2539096853673231757">"Sakinisha"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"kuweka njia za mkato"</string>
@@ -64,7 +65,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Hii ni programu ya mfumo na haiwezi kuondolewa."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Folda isiyo na jina"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> imezimwa"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ina arifa <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g></item>
       <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, ina arifa <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g></item>
     </plurals>
@@ -81,27 +82,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Folda: <xliff:g id="NAME">%1$s</xliff:g>"</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>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Imezimwa na msimamizi wako"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Ruhusu kuzungusha skrini ya Kwanza"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Simu inapozungushwa"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Vitone vya arifa"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Imewashwa"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Imezimwa"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Vitone vya arifa"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Imewashwa"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Imezimwa"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Inahitaji idhini ya kufikia arifa"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Ili kuonyesha Vitone vya Arifa, washa kipengele cha arifa za programu katika <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Badilisha mipangilio"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Onyesha kitone cha arifa"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Onyesha vitone vya arifa"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Ongeza aikoni kwenye Skrini ya kwanza"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Kwa ajili ya programu mpya"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Badilisha umbo la aikoni"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"kwenye Skrini ya mwanzo"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Tumia umbo chaguomsingi la mfumo"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Mraba"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Mstatili wenye pembe duara"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Mduara"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Umbo la chozi"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Inabadilisha umbo la aikoni"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Yasiyojulikana"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Ondoa"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Tafuta"</string>
@@ -112,10 +106,11 @@
     <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">"Ongeza 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>
+    <string name="undo" msgid="4151576204245173321">"Tendua"</string>
     <string name="action_move" msgid="4339390619886385032">"Hamisha kipengee"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Hamishia safu mlalo <xliff:g id="NUMBER_0">%1$s</xliff:g> safu wima <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Hamishia nafasi ya <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
@@ -146,4 +141,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Vipenge vya arifa na programu vimezimwa"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Funga"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Imefungwa"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"Hitilafu: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml
index b211207..691219a 100644
--- a/res/values-sw720dp/dimens.xml
+++ b/res/values-sw720dp/dimens.xml
@@ -16,7 +16,6 @@
 
 <resources>
     <!-- All Apps -->
-    <dimen name="all_apps_button_scale_down">8dp</dimen>
     <dimen name="all_apps_empty_search_message_top_offset">64dp</dimen>
     <dimen name="all_apps_empty_search_bg_top_offset">180dp</dimen>
 
diff --git a/res/values-ta-rIN/strings.xml b/res/values-ta-rIN/strings.xml
deleted file mode 100644
index a51247c..0000000
--- a/res/values-ta-rIN/strings.xml
+++ /dev/null
@@ -1,146 +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">
-    <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
-    <string name="folder_name" msgid="7371454440695724752"></string>
-    <string name="work_folder_name" msgid="3753320833950115786">"பணியிடம்"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"பயன்பாடு நிறுவப்படவில்லை."</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"பயன்பாடு இல்லை"</string>
-    <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="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="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="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="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="permlab_install_shortcut" msgid="5632423390354674437">"குறுக்குவழிகளை நிறுவுதல்"</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="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>
-    <string name="gadget_setup_text" msgid="8274003207686040488">"அமைவு"</string>
-    <string name="uninstall_system_app_text" msgid="4172046090762920660">"இது அமைப்பு பயன்பாடு என்பதால் நிறுவல் நீக்கம் செய்ய முடியாது."</string>
-    <string name="folder_hint_text" msgid="6617836969016293992">"பெயரிடப்படாத கோப்புறை"</string>
-    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> முடக்கப்பட்டது"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> பயன்பாட்டில், <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> அறிவிப்புகள் வந்துள்ளன</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> பயன்பாட்டில், <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> அறிவிப்பு வந்துள்ளது</item>
-    </plurals>
-    <string name="default_scroll_format" msgid="7475544710230993317">"பக்கம் %1$d / %2$d"</string>
-    <string name="workspace_scroll_format" msgid="8458889198184077399">"முகப்புத் திரை %1$d of %2$d"</string>
-    <string name="workspace_new_page" msgid="257366611030256142">"புதிய முகப்புத் திரை பக்கம்"</string>
-    <string name="folder_opened" msgid="94695026776264709">"திறக்கப்பட்டக் கோப்புறை, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="4625795376335528256">"கோப்புறையை மூட, தட்டவும்"</string>
-    <string name="folder_tap_to_rename" msgid="4017685068016979677">"மாற்றிய பெயரைச் சேமிக்க, தட்டவும்"</string>
-    <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" msgid="6629239338071103179">"கோப்புறை: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"ஷார்ட்கட்ஸ்"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"வால்பேப்பர்கள்"</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="icon_badging_title" msgid="874121399231955394">"அறிவிப்புப் புள்ளிகள்"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"ஆன்"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"முடக்கப்பட்டுள்ளது"</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="icon_badging_service_title" msgid="2309733118428242174">"அறிவிப்புப் புள்ளிகளைக் காட்டு"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"முகப்புத் திரையில் ஐகானைச் சேர்"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"புதிய பயன்பாடுகளுக்கு"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"ஐகான் வடிவத்தை மாற்று"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"முகப்புத் திரையில்"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"அமைப்பின் இயல்புநிலையைப் பயன்படுத்து"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"சதுரம்"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"சதுரவட்டம்"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"வட்டம்"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"கண்ணீர்துளி"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"ஐகான் வடிவத்தை மாற்றுகிறது"</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_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="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="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="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>
-    <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_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g>க்கான <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> குறுக்குவழிகள்"</string>
-    <string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> பயன்பாட்டிற்கான <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> குறுக்குவழிகளும் <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> அறிவிப்புகளும் உள்ளன"</string>
-    <string name="action_dismiss_notification" msgid="5909461085055959187">"நிராகரி"</string>
-    <string name="notification_dismissed" msgid="6002233469409822874">"அறிவிப்பு நிராகரிக்கப்பட்டது"</string>
-    <string name="all_apps_personal_tab" msgid="4190252696685155002">"தனிப்பட்டவை"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"பணி"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"பணி விவரம்"</string>
-    <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"பணி ஆப்ஸை இங்கு காணலாம்"</string>
-    <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"ஒவ்வொரு பணிப் பயன்பாடும் ஒரு பேட்ஜைக் கொண்டிருக்கும். இவை, ஆப்ஸ் உங்கள் நிறுவனத்தால் பாதுகாப்பாக வைக்கப்பட்டுள்ளன என்பதைக் குறிக்கின்றன. இந்த ஆப்ஸை எளிதாக அணுக, முகப்புத் திரைக்கு நகர்த்திக்கொள்ளவும்."</string>
-    <string name="work_mode_on_label" msgid="4781128097185272916">"உங்கள் நிறுவனம் நிர்வகிக்கிறது"</string>
-    <string name="work_mode_off_label" msgid="3194894777601421047">"ஆப்ஸும் அறிவிப்புகளும் ஆஃப் செய்யப்பட்டுள்ளன"</string>
-    <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"மூடுக"</string>
-    <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"மூடப்பட்டது"</string>
-</resources>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 983a0d1..672665e 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -22,11 +22,11 @@
     <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="work_folder_name" msgid="3753320833950115786">"பணியிடம்"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"பயன்பாடு நிறுவப்படவில்லை."</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"பயன்பாடு இல்லை"</string>
-    <string name="safemode_shortcut_error" msgid="9160126848219158407">"இறக்கிய பயன்பாடு பாதுகாப்பு முறையில் முடக்கப்பட்டது"</string>
+    <string name="activity_not_found" msgid="8071924732094499514">"ஆப்ஸ் நிறுவப்படவில்லை."</string>
+    <string name="activity_not_available" msgid="7456344436509528827">"ஆப்ஸ் இல்லை"</string>
+    <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="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>
@@ -39,6 +39,7 @@
     <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>
@@ -50,23 +51,23 @@
     <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="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>
-    <string name="uninstall_system_app_text" msgid="4172046090762920660">"இது அமைப்பு பயன்பாடு என்பதால் நிறுவல் நீக்கம் செய்ய முடியாது."</string>
+    <string name="uninstall_system_app_text" msgid="4172046090762920660">"இது அமைப்பு ஆப்ஸ் என்பதால் நிறுவல் நீக்கம் செய்ய முடியாது."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"பெயரிடப்படாத கோப்புறை"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> முடக்கப்பட்டது"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> பயன்பாட்டில், <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> அறிவிப்புகள் வந்துள்ளன</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> பயன்பாட்டில், <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> அறிவிப்பு வந்துள்ளது</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ஆப்ஸில் <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> அறிவிப்புகள் வந்துள்ளன</item>
+      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> ஆப்ஸில் <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> அறிவிப்பு வந்துள்ளது</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"பக்கம் %1$d / %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"முகப்புத் திரை %1$d of %2$d"</string>
@@ -77,45 +78,37 @@
     <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" msgid="6629239338071103179">"கோப்புறை: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"ஷார்ட்கட்ஸ்"</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_desc" msgid="8662546029078692509">"மொபைலைச் சுழற்றும் போது"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"அறிவிப்புப் புள்ளிகள்"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"ஆன்"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"முடக்கப்பட்டுள்ளது"</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="icon_badging_service_title" msgid="2309733118428242174">"அறிவிப்புப் புள்ளிகளைக் காட்டு"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"அறிவிப்புப் புள்ளிகளைக் காட்டு"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"முகப்புத் திரையில் ஐகானைச் சேர்"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"புதிய பயன்பாடுகளுக்கு"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"ஐகான் வடிவத்தை மாற்று"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"முகப்புத் திரையில்"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"அமைப்பின் இயல்புநிலையைப் பயன்படுத்து"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"சதுரம்"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"சதுரவட்டம்"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"வட்டம்"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"கண்ணீர்துளி"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"ஐகான் வடிவத்தை மாற்றுகிறது"</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="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_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>
-    <!-- no translation found for widgets_list (796804551140113767) -->
-    <skip />
-    <!-- no translation found for widgets_list_closed (6141506579418771922) -->
-    <skip />
+    <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="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>
@@ -133,17 +126,18 @@
     <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="action_deep_shortcut" msgid="2864038805849372848">"ஷார்ட்கட்கள்"</string>
     <string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"ஷார்ட்கட்கள் மற்றும் அறிவிப்புகள்"</string>
     <string name="action_dismiss_notification" msgid="5909461085055959187">"நிராகரி"</string>
     <string name="notification_dismissed" msgid="6002233469409822874">"அறிவிப்பு நிராகரிக்கப்பட்டது"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"தனிப்பட்டவை"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"பணி"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"பணி விவரம்"</string>
+    <string name="work_profile_toggle_label" msgid="3081029915775481146">"பணிக் கணக்கு"</string>
     <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"பணி ஆப்ஸை இங்கு காணலாம்"</string>
     <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"ஒவ்வொரு பணிப் பயன்பாடும் ஒரு பேட்ஜைக் கொண்டிருக்கும். இவை, ஆப்ஸ் உங்கள் நிறுவனத்தால் பாதுகாப்பாக வைக்கப்பட்டுள்ளன என்பதைக் குறிக்கின்றன. இந்த ஆப்ஸை எளிதாக அணுக, முகப்புத் திரைக்கு நகர்த்திக்கொள்ளவும்."</string>
     <string name="work_mode_on_label" msgid="4781128097185272916">"உங்கள் நிறுவனம் நிர்வகிக்கிறது"</string>
     <string name="work_mode_off_label" msgid="3194894777601421047">"ஆப்ஸும் அறிவிப்புகளும் ஆஃப் செய்யப்பட்டுள்ளன"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"மூடுக"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"மூடப்பட்டது"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"தோல்வி: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-te-rIN/strings.xml b/res/values-te-rIN/strings.xml
deleted file mode 100644
index bffb1a8..0000000
--- a/res/values-te-rIN/strings.xml
+++ /dev/null
@@ -1,146 +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">
-    <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
-    <string name="folder_name" msgid="7371454440695724752"></string>
-    <string name="work_folder_name" msgid="3753320833950115786">"కార్యాలయం"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"యాప్ ఇన్‌స్టాల్ చేయబడలేదు."</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"యాప్ అందుబాటులో లేదు"</string>
-    <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="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">"మాన్యువల్‌గా ఉంచడానికి నొక్కి &amp;amp పట్టుకోండి"</string>
-    <string name="place_automatically" msgid="8064208734425456485">"స్వయంచాలకంగా జోడించు"</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="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="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="permlab_install_shortcut" msgid="5632423390354674437">"సత్వరమార్గాలను ఇన్‌స్టాల్ చేయడం"</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="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>
-    <string name="gadget_setup_text" msgid="8274003207686040488">"సెటప్ చేయి"</string>
-    <string name="uninstall_system_app_text" msgid="4172046090762920660">"ఇది సిస్టమ్ యాప్ మరియు దీన్ని అన్‌ఇన్‌స్టాల్ చేయడం సాధ్యపడదు."</string>
-    <string name="folder_hint_text" msgid="6617836969016293992">"పేరు లేని ఫోల్డర్"</string>
-    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> నిలిపివేయబడింది"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> నోటిఫికేషన్‌‌లను కలిగి ఉన్నారు</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> నోటిఫికేషన్‌ను కలిగి ఉన్నారు</item>
-    </plurals>
-    <string name="default_scroll_format" msgid="7475544710230993317">"%2$dలో %1$dవ పేజీ"</string>
-    <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$dలో %1$dవ హోమ్ స్క్రీన్"</string>
-    <string name="workspace_new_page" msgid="257366611030256142">"కొత్త హోమ్ స్క్రీన్ పేజీ"</string>
-    <string name="folder_opened" msgid="94695026776264709">"ఫోల్డర్ తెరవబడింది, <xliff:g id="WIDTH">%1$d</xliff:g> X <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="4625795376335528256">"ఫోల్డర్‌ను మూసివేయడానికి నొక్కండి"</string>
-    <string name="folder_tap_to_rename" msgid="4017685068016979677">"పేరు మార్పును సేవ్ చేయడానికి నొక్కండి"</string>
-    <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" msgid="6629239338071103179">"ఫోల్డర్: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"విడ్జెట్‌లు"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"వాల్‌పేపర్‌లు"</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="icon_badging_title" msgid="874121399231955394">"నోటిఫికేషన్ డాట్‌లు"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"ఆన్"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"ఆఫ్"</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="icon_badging_service_title" msgid="2309733118428242174">"నోటిఫికేషన్ డాట్‌లను చూపుతుంది"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"హోమ్ స్క్రీన్‌కి చిహ్నాన్ని జోడించు"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"కొత్త యాప్‌ల కోసం"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"చిహ్న ఆకారాన్ని మార్చు"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"హోమ్ స్క్రీన్‌పై"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"సిస్టమ్ డిఫాల్ట్‌ను ఉపయోగించండి"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"చతురస్రం"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"చతురస్రాకార వృత్తం"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"వృత్తం"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"కన్నీటి చుక్క"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"చిహ్న ఆకార మార్పులను వర్తింపజేస్తోంది"</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_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="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="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="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>
-    <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_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> కోసం <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> సత్వరమార్గాలు"</string>
-    <string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> కోసం <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> సత్వరమార్గాలు మరియు <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> నోటిఫికేషన్‌లు"</string>
-    <string name="action_dismiss_notification" msgid="5909461085055959187">"తీసివేయి"</string>
-    <string name="notification_dismissed" msgid="6002233469409822874">"నోటిఫికేషన్ తీసివేయబడింది"</string>
-    <string name="all_apps_personal_tab" msgid="4190252696685155002">"వ్యక్తిగతం"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"కార్యాలయం"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"కార్యాలయ ప్రొఫైల్"</string>
-    <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"కార్యాలయ యాప్‌లను ఇక్కడ కనుగొనండి"</string>
-    <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"ప్రతి కార్యాలయ యాప్‌కు బ్యాడ్జ్‌ ఉంది మరియు మీ సంస్థ ద్వారా సురక్షితంగా ఉంచబడుతుంది. సులభ యాక్సెస్ కోసం యాప్‌లను మీ హోమ్ స్క్రీన్‌కి తరలించండి."</string>
-    <string name="work_mode_on_label" msgid="4781128097185272916">"మీ సంస్థ ద్వారా నిర్వహించబడతాయి"</string>
-    <string name="work_mode_off_label" msgid="3194894777601421047">"నోటిఫికేషన్‌లు మరియు యాప్‌లు ఆఫ్ చేయబడ్డాయి"</string>
-    <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"మూసివేయి"</string>
-    <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"మూసివేయబడింది"</string>
-</resources>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index a18d649..c8f70fe 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -33,12 +33,13 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"విడ్జెట్‌ను ఎంచుకోవడానికి లేదా అనుకూల చర్యలను ఉపయోగించడానికి రెండుసార్లు నొక్కి, ఉంచండి."</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">"మాన్యువల్‌గా ఉంచడానికి నొక్కి &amp;amp పట్టుకోండి"</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>
     <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>
@@ -64,9 +65,9 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"ఇది సిస్టమ్ యాప్ మరియు దీన్ని అన్‌ఇన్‌స్టాల్ చేయడం సాధ్యపడదు."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"పేరు లేని ఫోల్డర్"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> నిలిపివేయబడింది"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> నోటిఫికేషన్‌‌లను కలిగి ఉన్నారు</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> నోటిఫికేషన్‌ను కలిగి ఉన్నారు</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, నుంచి <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> నోటిఫికేషన్‌లు ఉన్నాయి</item>
+      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, నుంచి <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> నోటిఫికేషన్ ఉంది</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"%2$dలో %1$dవ పేజీ"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$dలో %1$dవ హోమ్ స్క్రీన్"</string>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"ఫోల్డర్: <xliff:g id="NAME">%1$s</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_desc" msgid="8662546029078692509">"ఫోన్‌‌ను తిప్పినప్పుడు"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"నోటిఫికేషన్ డాట్‌లు"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"ఆన్"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"ఆఫ్"</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="icon_badging_service_title" msgid="2309733118428242174">"నోటిఫికేషన్ డాట్‌లను చూపుతుంది"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"నోటిఫికేషన్ డాట్‌లను చూపు"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"హోమ్ స్క్రీన్‌కి చిహ్నాన్ని జోడించు"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"కొత్త యాప్‌ల కోసం"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"చిహ్న ఆకారాన్ని మార్చు"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"హోమ్ స్క్రీన్‌పై"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"సిస్టమ్ డిఫాల్ట్‌ను ఉపయోగించండి"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"చతురస్రం"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"చతురస్రాకార వృత్తం"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"వృత్తం"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"కన్నీటి చుక్క"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"చిహ్న ఆకార మార్పులను వర్తింపజేస్తోంది"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"తెలియదు"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"తీసివేయి"</string>
     <string name="abandoned_search" msgid="891119232568284442">"వెతుకు"</string>
@@ -108,14 +102,13 @@
     <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>
-    <!-- no translation found for widgets_list (796804551140113767) -->
-    <skip />
-    <!-- no translation found for widgets_list_closed (6141506579418771922) -->
-    <skip />
+    <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="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>
@@ -146,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"నోటిఫికేషన్‌లు మరియు యాప్‌లు ఆఫ్ చేయబడ్డాయి"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"మూసివేయి"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"మూసివేయబడింది"</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 2da32de..03c02ad 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,7 +65,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"นี่เป็นแอประบบและไม่สามารถถอนการติดตั้งได้"</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"โฟลเดอร์ที่ไม่มีชื่อ"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"ปิดใช้ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> มีการแจ้งเตือน <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> รายการ</item>
       <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> มีการแจ้งเตือน <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> รายการ</item>
     </plurals>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"โฟลเดอร์: <xliff:g id="NAME">%1$s</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="icon_badging_title" msgid="874121399231955394">"จุดแจ้งเตือน"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"เปิด"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"ปิด"</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="icon_badging_service_title" msgid="2309733118428242174">"แสดงจุดแจ้งเตือน"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"แสดงเครื่องหมายจุดแสดงการแจ้งเตือน"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"เพิ่มไอคอนในหน้าจอหลัก"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"สำหรับแอปใหม่"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"เปลี่ยนรูปร่างไอคอน"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"ในหน้าจอหลัก"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"ใช้ค่าเริ่มต้นของระบบ"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"สี่เหลี่ยมจัตุรัส"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"สี่เหลี่ยมขอบมน"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"วงกลม"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"หยดน้ำตา"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"กำลังนำการเปลี่ยนรูปร่างไอคอนไปใช้"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"ไม่รู้จัก"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ลบ"</string>
     <string name="abandoned_search" msgid="891119232568284442">"ค้นหา"</string>
@@ -110,10 +104,11 @@
     <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="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>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"ปิดการแจ้งเตือนและแอปอยู่"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"ปิด"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"ปิด"</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 68d5259..0df94c7 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -33,12 +33,13 @@
     <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="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 upang manual na ilagay"</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>
     <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>
@@ -64,9 +65,9 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Isa itong app ng system at hindi maaaring i-uninstall."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Walang Pangalang Folder"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Naka-disable ang <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="one">May <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notification ang <xliff:g id="APP_NAME_2">%1$s</xliff:g></item>
-      <item quantity="other">May <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> na notification ang <xliff:g id="APP_NAME_2">%1$s</xliff:g></item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="one">May <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> (na) notification ang <xliff:g id="APP_NAME_2">%1$s</xliff:g></item>
+      <item quantity="other">May <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> (na) notification ang <xliff:g id="APP_NAME_2">%1$s</xliff:g></item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"Pahina %1$d ng %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Home screen %1$d ng %2$d"</string>
@@ -79,32 +80,25 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</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>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Na-disable ng iyong admin"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Payagan ang pag-rotate ng Home screen"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Kailan maro-rotate ang telepono"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Mga notification dot"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Naka-on"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Naka-off"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Mga notification dot"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Naka-on"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Naka-off"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Kinakailangan ng access sa notification"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Upang ipakita ang Mga Notification Dot, i-on ang mga notification ng app para sa <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Baguhin ang mga setting"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Ipakita ang mga notification dot"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Ipakita ang mga notification dot"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Idagdag ang icon sa Home screen"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Para sa mga bagong app"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Baguhin ang hugis ng icon"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"sa Home screen"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Gamitin ang default ng system"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Parisukat"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Squircle"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Bilog"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Teardrop"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Inilalapat ang mga pagbabago sa hugis ng icon"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Hindi kilala"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Alisin"</string>
     <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. Maaari mo itong alisin, o maaari mong hanapin ang app at i-install ito nang manu-mano."</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_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>
@@ -114,6 +108,7 @@
     <string name="action_move_here" msgid="2170188780612570250">"Ilipat ang item dito"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Naidagdag sa home screen ang item"</string>
     <string name="item_removed" msgid="851119963877842327">"Naalis na ang item"</string>
+    <string name="undo" msgid="4151576204245173321">"I-undo"</string>
     <string name="action_move" msgid="4339390619886385032">"Ilipat ang item"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Ilipat sa row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Ilipat sa posisyon <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Naka-off ang mga notification at app"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Isara"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Nakasara"</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 af121f2..56f8447 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,7 +65,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Bu bir sistem uygulamasıdır ve yüklemesi kaldırılamaz."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Adsız Klasör"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> devre dışı"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> uygulamasının <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> bildirimi var</item>
       <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> uygulamasının <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> bildirimi var</item>
     </plurals>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Klasör: <xliff:g id="NAME">%1$s</xliff:g>"</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>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Yöneticiniz tarafından devre dışı bırakıldı"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Ana ekranı döndürmeye izin ver"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Telefon döndürüldüğünde"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Bildirim noktaları"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Açık"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Kapalı"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Bildirim noktaları"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Açık"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Kapalı"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Bildirim erişimi gerekli"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Bildirim Noktaları\'nı göstermek için <xliff:g id="NAME">%1$s</xliff:g> uygulamasının bildirimlerini açın"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Ayarları değiştir"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Bildirim noktalarını göster"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Bildirim noktalarını göster"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Ana ekrana simge ekle"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Yeni uygulamalar için"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Simge şeklini değiştir"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"Ana ekranda"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Sistem varsayılanını kullan"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Kare"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Kare-daire"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Daire"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Gözyaşı damlası"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Simge şekli değişiklikleri uygulanıyor"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Bilinmiyor"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Kaldır"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Ara"</string>
@@ -114,6 +108,7 @@
     <string name="action_move_here" msgid="2170188780612570250">"Öğeyi buraya taşı"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Öğe ana ekrana eklendi"</string>
     <string name="item_removed" msgid="851119963877842327">"Öğe kaldırıldı"</string>
+    <string name="undo" msgid="4151576204245173321">"Geri al"</string>
     <string name="action_move" msgid="4339390619886385032">"Öğeyi taşı"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g>. satır <xliff:g id="NUMBER_1">%2$s</xliff:g>. sütuna taşı"</string>
     <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g>. sıraya taşı"</string>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Bildirimler ve uygulamalar kapalı"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Kapat"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Kapalı"</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 305c9aa..13ba701 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -49,7 +50,7 @@
     <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="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="permlab_install_shortcut" msgid="5632423390354674437">"створення ярликів"</string>
@@ -64,7 +65,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Це системна програма, її неможливо видалити."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Папка без назви"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> вимкнено"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="one">Додаток <xliff:g id="APP_NAME_2">%1$s</xliff:g> має <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> сповіщення</item>
       <item quantity="few">Додаток <xliff:g id="APP_NAME_2">%1$s</xliff:g> має <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> сповіщення</item>
       <item quantity="many">Додаток <xliff:g id="APP_NAME_2">%1$s</xliff:g> має <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> сповіщень</item>
@@ -81,29 +82,22 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Папка <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Віджети"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Фонові малюнки"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Налаштування Home"</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="icon_badging_title" msgid="874121399231955394">"Значки сповіщень"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Увімкнено"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Вимкнено"</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="icon_badging_service_title" msgid="2309733118428242174">"Показувати значки сповіщень"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Додати значок на головний екран"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Показувати значки сповіщень"</string>
+    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Додавати значок на головний екран"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Для нових додатків"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Змінити форму значка"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"на головному екрані"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Використовувати налаштування системи за умовчанням"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Квадрат"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Квадрат із заокругленими кутами"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Круг"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Сльоза"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Змінюється форма значка"</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>
@@ -116,6 +110,7 @@
     <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>
@@ -146,4 +141,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Сповіщення та додатки вимкнено"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Закрити"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Закрито"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"Не вдалося <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-ur-rPK/strings.xml b/res/values-ur-rPK/strings.xml
deleted file mode 100644
index 77aa0a1..0000000
--- a/res/values-ur-rPK/strings.xml
+++ /dev/null
@@ -1,146 +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">
-    <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
-    <string name="folder_name" msgid="7371454440695724752"></string>
-    <string name="work_folder_name" msgid="3753320833950115786">"دفتری"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"ایپ انسٹال نہیں ہے۔"</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"ایپ دستیاب نہیں ہے"</string>
-    <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="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">"‏دستی طور پر رکھنے کیلئے ‎&amp; ٹچ کرکے ہولڈ کریں"</string>
-    <string name="place_automatically" msgid="8064208734425456485">"خود کار طور پر شامل کریں"</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="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="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="permlab_install_shortcut" msgid="5632423390354674437">"شارٹ کٹس انسٹال کریں"</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="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>
-    <string name="gadget_setup_text" msgid="8274003207686040488">"ترتیب دیں"</string>
-    <string name="uninstall_system_app_text" msgid="4172046090762920660">"یہ ایک سسٹم ایپ ہے اور اسے اَن انسٹال نہیں کیا جا سکتا ہے۔"</string>
-    <string name="folder_hint_text" msgid="6617836969016293992">"بلا نام فولڈر"</string>
-    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> غیر فعال ہے"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> میں <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> اطلاعات ہیں</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> میں <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> اطلاع ہے</item>
-    </plurals>
-    <string name="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_name_format" msgid="6629239338071103179">"فولڈر: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"ویجیٹس"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"وال پیپرز"</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="icon_badging_title" msgid="874121399231955394">"اطلاعاتی ڈاٹس"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"آن"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"آف"</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="icon_badging_service_title" msgid="2309733118428242174">"اطلاعاتی ڈاٹس دکھائیں"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"آئیکن کو ہوم اسکرین میں شامل کریں"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"نئی ایپس کیلئے"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"آئیکن کی شکل تبدیل کریں"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"ہوم اسکرین پر"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"سسٹم ڈیفالٹ کا استعمال کریں"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"مربع"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"اسکورکل"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"حلقہ"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"آنسو کا قطرہ"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"آئيکن کی شکل کی تبدیلیاں لاگو ہو رہی ہیں"</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_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="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="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="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>
-    <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_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> کیلئے <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> شارٹ کٹس"</string>
-    <string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> کے <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> شارٹ کٹس اور <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> اطلاعات"</string>
-    <string name="action_dismiss_notification" msgid="5909461085055959187">"برخاست کریں"</string>
-    <string name="notification_dismissed" msgid="6002233469409822874">"اطلاع مسترد ہو گئی"</string>
-    <string name="all_apps_personal_tab" msgid="4190252696685155002">"ذاتی"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"دفتری"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"دفتری پروفائل"</string>
-    <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"یہاں دفتری ایپس تلاش کریں"</string>
-    <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"ہر دفتری ایپ میں ایک بَیج ہوتا ہے اور اسے آپ کی تنظیم محفوظ رکھتی ہے۔ زیادہ آسان رسائی کیلئے ایپس کو اپنی ہوم اسکرین پر منتقل کریں۔"</string>
-    <string name="work_mode_on_label" msgid="4781128097185272916">"آپ کی تنظیم کے زیر انتظام"</string>
-    <string name="work_mode_off_label" msgid="3194894777601421047">"اطلاعات اور ایپس آف ہیں"</string>
-    <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"بند کریں"</string>
-    <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"بند"</string>
-</resources>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 7853fd4..4f77670 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -33,12 +33,13 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"کوئی ویجٹ منتخب کرنے یا حسب ضرورت کاروائیاں استعمال کرنے کیلئے دو بار تھپتھپائیں اور پکڑے رکھیں۔"</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">"‏دستی طور پر رکھنے کیلئے ‎&amp; ٹچ کرکے ہولڈ کریں"</string>
+    <string name="add_item_request_drag_hint" msgid="5899764264480397019">"‏دستی طور پر رکھنے کیلئے ‎ٹچ کر کے دبائے رکھیں"</string>
     <string name="place_automatically" msgid="8064208734425456485">"خود کار طور پر شامل کریں"</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>
@@ -64,9 +65,9 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"یہ ایک سسٹم ایپ ہے اور اسے اَن انسٹال نہیں کیا جا سکتا ہے۔"</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"بلا نام فولڈر"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> غیر فعال ہے"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> میں <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> اطلاعات ہیں</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> میں <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> اطلاع ہے</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> میں<xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> اطلاعات ہیں</item>
+      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> میں<xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> اطلاع ہے</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"‏صفحہ ‎%1$d از ‎%2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"‏ہوم اسکرین ‎%1$d از ‎%2$d"</string>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"فولڈر: <xliff:g id="NAME">%1$s</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="icon_badging_title" msgid="874121399231955394">"اطلاعاتی ڈاٹس"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"آن"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"آف"</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="icon_badging_service_title" msgid="2309733118428242174">"اطلاعاتی ڈاٹس دکھائیں"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"اطلاعاتی ڈاٹس دکھائیں"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"آئیکن کو ہوم اسکرین میں شامل کریں"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"نئی ایپس کیلئے"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"آئیکن کی شکل تبدیل کریں"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"ہوم اسکرین پر"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"سسٹم ڈیفالٹ کا استعمال کریں"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"مربع"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"اسکورکل"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"حلقہ"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"آنسو کا قطرہ"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"آئيکن کی شکل کی تبدیلیاں لاگو ہو رہی ہیں"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"نامعلوم"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ہٹائیں"</string>
     <string name="abandoned_search" msgid="891119232568284442">"تلاش کریں"</string>
@@ -108,14 +102,13 @@
     <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>
-    <!-- no translation found for widgets_list (796804551140113767) -->
-    <skip />
-    <!-- no translation found for widgets_list_closed (6141506579418771922) -->
-    <skip />
+    <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="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>
@@ -146,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"اطلاعات اور ایپس آف ہیں"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"بند کریں"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"بند"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"ناکام ہو گيا: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-uz-rUZ/strings.xml b/res/values-uz-rUZ/strings.xml
deleted file mode 100644
index 0360a71..0000000
--- a/res/values-uz-rUZ/strings.xml
+++ /dev/null
@@ -1,146 +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">
-    <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
-    <string name="folder_name" msgid="7371454440695724752"></string>
-    <string name="work_folder_name" msgid="3753320833950115786">"Ishga oid"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"Ilova o‘rnatilmadi."</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"Ilova mavjud emas"</string>
-    <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="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">"Qo‘lda joylashtirish uchun bosib turing"</string>
-    <string name="place_automatically" msgid="8064208734425456485">"Avtomatik chiqarish"</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="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="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">"Ishchi 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>
-    <string name="install_drop_target_label" msgid="2539096853673231757">"O‘rnatish"</string>
-    <string name="permlab_install_shortcut" msgid="5632423390354674437">"yorliqlar yaratish"</string>
-    <string name="permdesc_install_shortcut" msgid="923466509822011139">"Ilovalarga foydalanuvchidan so‘ramasdan yorliqlar qo‘shishga ruxsat beradi."</string>
-    <string name="permlab_read_settings" msgid="1941457408239617576">"Uy sozlamalari va yorliqlarini o‘qish"</string>
-    <string name="permdesc_read_settings" msgid="5833423719057558387">"Ilovaga \"Uy\" ekranidagi yorliqlar va sozlamalarni o‘qish uchun ruxsat beradi."</string>
-    <string name="permlab_write_settings" msgid="3574213698004620587">"Uy sozlamalari va yorliqlarini yozish"</string>
-    <string name="permdesc_write_settings" msgid="5440712911516509985">"Ilovaga \"Uy\" ekranidagi yorliqlar va sozlamalrni o‘zgartirish uchun ruxsat beradi."</string>
-    <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasiga qo‘ng‘iroqlarni amalga oshirishga ruxsat berilmagan"</string>
-    <string name="gadget_error_text" msgid="6081085226050792095">"Vidjetni yuklashda muammo"</string>
-    <string name="gadget_setup_text" msgid="8274003207686040488">"Sozlash"</string>
-    <string name="uninstall_system_app_text" msgid="4172046090762920660">"Bu tizim ilovasi, shuning uchun o‘chirib bo‘lmaydi."</string>
-    <string name="folder_hint_text" msgid="6617836969016293992">"Nomsiz jild"</string>
-    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi o‘chirib qo‘yildi"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ta bildirishnoma bor</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> ta bildirishnoma bor</item>
-    </plurals>
-    <string name="default_scroll_format" msgid="7475544710230993317">"%2$ddan %1$d ta sahifa"</string>
-    <string name="workspace_scroll_format" msgid="8458889198184077399">"Uy ekrani %2$ddan %1$d"</string>
-    <string name="workspace_new_page" msgid="257366611030256142">"Yangi bosh ekran sahifasi"</string>
-    <string name="folder_opened" msgid="94695026776264709">"Jild ochildi, <xliff:g id="WIDTH">%1$d</xliff:g> ga <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="4625795376335528256">"Jildni yopish uchun ustiga bosing"</string>
-    <string name="folder_tap_to_rename" msgid="4017685068016979677">"O‘zgarishni saqlash uchun ustiga bosing"</string>
-    <string name="folder_closed" msgid="4100806530910930934">"Jild yopildi"</string>
-    <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" msgid="6629239338071103179">"Jild: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Vidjetlar"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"Fon rasmlari"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Bosh ekran sozlamalari"</string>
-    <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administrator tomonidan o‘chirilgan"</string>
-    <string name="allow_rotation_title" msgid="7728578836261442095">"Asosiy ekranni aylantirishga ruxsat berish"</string>
-    <string name="allow_rotation_desc" msgid="8662546029078692509">"Telefon burilganda"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Bildirishnoma belgilari"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Yoniq"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"O‘chiq"</string>
-    <string name="title_missing_notification_access" msgid="7503287056163941064">"Bildirishnomalarga ruxsat berilmagan"</string>
-    <string name="msg_missing_notification_access" msgid="281113995110910548">"Bildirishnoma belgilarini ko‘rsatish uchun <xliff:g id="NAME">%1$s</xliff:g> ilovasida bildirishnomalarni yoqing"</string>
-    <string name="title_change_settings" msgid="1376365968844349552">"Sozlamalarni o‘zgartirish"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Bildirishnoma belgilarini ko‘rsatish"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Bosh ekranga ikonka chiqarish"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Yangi o‘rnatilgan ilovalar ikonkasini bosh ekranga chiqarish"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Ikonka shaklini o‘zgartirish"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"Bosh ekranda"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Standart tizim parametrlaridan foydalanish"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Qirralari aylana kvadrat"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Aylana"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Tomchi"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Ikonka shakli o‘zgartirilmoqda"</string>
-    <string name="package_state_unknown" msgid="7592128424511031410">"Noma’lum"</string>
-    <string name="abandoned_clean_this" msgid="7610119707847920412">"O‘chirish"</string>
-    <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_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="action_add_to_workspace" msgid="8902165848117513641">"Bosh ekranga qo‘shish"</string>
-    <string name="action_move_here" msgid="2170188780612570250">"Obyektni bu yerga ko‘chirish"</string>
-    <string name="item_added_to_workspace" msgid="4211073925752213539">"Obyekt bosh ekranga qo‘shildi"</string>
-    <string name="item_removed" msgid="851119963877842327">"Obyekt o‘chirib tashlandi"</string>
-    <string name="action_move" msgid="4339390619886385032">"Obyektni ko‘chirib o‘tkazish"</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> katakka ko‘chirib o‘tkazish"</string>
-    <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g>-joyga ko‘chirib o‘tkazish"</string>
-    <string name="move_to_hotseat_position" msgid="6295412897075147808">"Sevimlilarga (<xliff:g id="NUMBER">%1$s</xliff:g>) ko‘chirib o‘tkazish"</string>
-    <string name="item_moved" msgid="4606538322571412879">"Element ko‘chirib o‘tkazildi"</string>
-    <string name="add_to_folder" msgid="9040534766770853243">"<xliff:g id="NAME">%1$s</xliff:g> jildiga qo‘shish"</string>
-    <string name="add_to_folder_with_app" msgid="4534929978967147231">"<xliff:g id="NAME">%1$s</xliff:g> ilovasi bor jildga qo‘shish"</string>
-    <string name="added_to_folder" msgid="4793259502305558003">"Element jildga qo‘shildi"</string>
-    <string name="create_folder_with" msgid="4050141361160214248">"<xliff:g id="NAME">%1$s</xliff:g> bilan jild yaratish"</string>
-    <string name="folder_created" msgid="6409794597405184510">"Jild yaratildi"</string>
-    <string name="action_move_to_workspace" msgid="1603837886334246317">"Bosh ekranga ko‘chirish"</string>
-    <string name="action_resize" msgid="1802976324781771067">"O‘lchamini o‘zgartirish"</string>
-    <string name="action_increase_width" msgid="8773715375078513326">"Enini uzaytirish"</string>
-    <string name="action_increase_height" msgid="459390020612501122">"Bo‘yini uzaytirish"</string>
-    <string name="action_decrease_width" msgid="1374549771083094654">"Enini kichraytirish"</string>
-    <string name="action_decrease_height" msgid="282377193880900022">"Bo‘yini kichraytirish"</string>
-    <string name="widget_resized" msgid="9130327887929620">"Vidjetning eni <xliff:g id="NUMBER_0">%1$s</xliff:g>, bo‘yi <xliff:g id="NUMBER_1">%2$s</xliff:g> qilib o‘zgartirildi"</string>
-    <string name="action_deep_shortcut" msgid="2864038805849372848">"Tezkor tugmalar"</string>
-    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> ilovasi uchun <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> ta tezkor tugma"</string>
-    <string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> ilovasi uchun <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> ta yorliq va <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> ta bildirishnoma"</string>
-    <string name="action_dismiss_notification" msgid="5909461085055959187">"Yopish"</string>
-    <string name="notification_dismissed" msgid="6002233469409822874">"Bildirishnoma yopildi"</string>
-    <string name="all_apps_personal_tab" msgid="4190252696685155002">"Shaxsiy"</string>
-    <string name="all_apps_work_tab" msgid="4884822796154055118">"Ishchi"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"Ishchi profil"</string>
-    <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Ishga oid ilovalarni shu yerdan topish mumkin"</string>
-    <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Nishonga ega har bir ishga oid ilova tashkilotingiz tomonidan himoyalanadi. Ishga oid ilovalarga osonroq kirish uchun ularni bosh ekranga chiqaring."</string>
-    <string name="work_mode_on_label" msgid="4781128097185272916">"Tashkilotingiz tomonidan boshqariladi"</string>
-    <string name="work_mode_off_label" msgid="3194894777601421047">"Bildirishnomalar va ilovalar faol emas"</string>
-    <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Yopish"</string>
-    <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Yopiq"</string>
-</resources>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index 287ec09..69084d7 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -33,12 +33,13 @@
     <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="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">"Qo‘lda joylashtirish uchun bosib turing"</string>
+    <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Joylash uchun bosib turing"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Avtomatik chiqarish"</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>
@@ -64,9 +65,9 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Bu tizim ilovasi, shuning uchun o‘chirib bo‘lmaydi."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Nomsiz jild"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi o‘chirib qo‘yildi"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ta bildirishnoma bor</item>
-      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> ta bildirishnoma bor</item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ilovasida <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ta bildirishnoma bor</item>
+      <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> ilovasida <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> ta bildirishnoma bor</item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"%2$ddan %1$d ta sahifa"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Uy ekrani %2$ddan %1$d"</string>
@@ -79,29 +80,22 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Jild: <xliff:g id="NAME">%1$s</xliff:g>"</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>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administrator tomonidan o‘chirilgan"</string>
-    <string name="allow_rotation_title" msgid="7728578836261442095">"Asosiy ekranni aylantirishga ruxsat berish"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Bosh ekranni burishga ruxsat"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Telefon burilganda"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Bildirishnoma belgilari"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Yoniq"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"O‘chiq"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Bildirishnoma belgilari"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Yoniq"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Yoqilmagan"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Bildirishnomalarga ruxsat berilmagan"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Bildirishnoma belgilarini ko‘rsatish uchun <xliff:g id="NAME">%1$s</xliff:g> ilovasida bildirishnomalarni yoqing"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Sozlamalarni o‘zgartirish"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Bildirishnoma belgilarini ko‘rsatish"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Bildirishnoma belgilarini chiqarish"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Bosh ekranga ikonka chiqarish"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Yangi o‘rnatilgan ilovalar ikonkasini bosh ekranga chiqarish"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Ikonka shaklini o‘zgartirish"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"Bosh ekranda"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Standart tizim parametrlaridan foydalanish"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Qirralari aylana kvadrat"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Aylana"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Tomchi"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Ikonka shakli o‘zgartirilmoqda"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Noma’lum"</string>
-    <string name="abandoned_clean_this" msgid="7610119707847920412">"O‘chirish"</string>
+    <string name="abandoned_clean_this" msgid="7610119707847920412">"Olib tashlash"</string>
     <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>
@@ -110,14 +104,15 @@
     <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 qo‘shish"</string>
+    <string name="action_add_to_workspace" msgid="8902165848117513641">"Bosh ekranga chiqarish"</string>
     <string name="action_move_here" msgid="2170188780612570250">"Obyektni bu yerga ko‘chirish"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Obyekt bosh ekranga qo‘shildi"</string>
     <string name="item_removed" msgid="851119963877842327">"Obyekt o‘chirib tashlandi"</string>
+    <string name="undo" msgid="4151576204245173321">"Qaytarish"</string>
     <string name="action_move" msgid="4339390619886385032">"Obyektni ko‘chirib o‘tkazish"</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> katakka ko‘chirib o‘tkazish"</string>
-    <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g>-joyga ko‘chirib o‘tkazish"</string>
-    <string name="move_to_hotseat_position" msgid="6295412897075147808">"Sevimlilarga (<xliff:g id="NUMBER">%1$s</xliff:g>) ko‘chirib o‘tkazish"</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> katakka olish"</string>
+    <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g>-joyga olish"</string>
+    <string name="move_to_hotseat_position" msgid="6295412897075147808">"Sevimlilarga olish (<xliff:g id="NUMBER">%1$s</xliff:g>)"</string>
     <string name="item_moved" msgid="4606538322571412879">"Element ko‘chirib o‘tkazildi"</string>
     <string name="add_to_folder" msgid="9040534766770853243">"<xliff:g id="NAME">%1$s</xliff:g> jildiga qo‘shish"</string>
     <string name="add_to_folder_with_app" msgid="4534929978967147231">"<xliff:g id="NAME">%1$s</xliff:g> ilovasi bor jildga qo‘shish"</string>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Bildirishnomalar va ilovalar faol emas"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Yopish"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Yopiq"</string>
+    <string name="remote_action_failed" msgid="1383965239183576790">"Xato: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-v19/styles.xml b/res/values-v19/styles.xml
deleted file mode 100644
index 36c0971..0000000
--- a/res/values-v19/styles.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?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.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT 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="LauncherTheme" parent="@style/BaseLauncherThemeWithCustomAttrs">
-        <item name="android:windowTranslucentStatus">true</item>
-        <item name="android:windowTranslucentNavigation">true</item>
-    </style>
-
-</resources>
\ No newline at end of file
diff --git a/res/values-v21/styles.xml b/res/values-v21/styles.xml
deleted file mode 100644
index 927719c..0000000
--- a/res/values-v21/styles.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?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.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT 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="LauncherTheme" parent="@style/BaseLauncherThemeWithCustomAttrs">
-        <item name="android:windowTranslucentStatus">false</item>
-        <item name="android:windowTranslucentNavigation">false</item>
-        <item name="android:windowDrawsSystemBarBackgrounds">true</item>
-        <item name="android:statusBarColor">#00000000</item>
-        <item name="android:navigationBarColor">#00000000</item>
-    </style>
-</resources>
diff --git a/res/values-v22/styles.xml b/res/values-v22/styles.xml
new file mode 100644
index 0000000..f86db7a
--- /dev/null
+++ b/res/values-v22/styles.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright (C) 2018 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+
+<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/bools.xml b/res/values-v26/bools.xml
index 30537fe..ad8c7a1 100644
--- a/res/values-v26/bools.xml
+++ b/res/values-v26/bools.xml
@@ -17,7 +17,7 @@
 -->
 
 <resources>
-    <bool name="notification_badging_enabled">true</bool>
+    <bool name="notification_dots_enabled">true</bool>
 
     <bool name="enable_install_shortcut_api">false</bool>
 </resources>
\ No newline at end of file
diff --git a/res/values-v26/styles.xml b/res/values-v26/styles.xml
index e810ab2..8fb408b 100644
--- a/res/values-v26/styles.xml
+++ b/res/values-v26/styles.xml
@@ -19,6 +19,7 @@
 <resources>
     <!-- Theme for the widget container. -->
     <style name="WidgetContainerTheme" parent="@android:style/Theme.DeviceDefault.Settings">
+        <item name="android:colorPrimaryDark">#E8EAED</item>
         <item name="android:colorEdgeEffect">?android:attr/textColorSecondary</item>
     </style>
     <style name="WidgetContainerTheme.Dark" parent="AppTheme.Dark">
diff --git a/res/values-v28/styles.xml b/res/values-v28/styles.xml
new file mode 100644
index 0000000..7df9ce5
--- /dev/null
+++ b/res/values-v28/styles.xml
@@ -0,0 +1,23 @@
+<?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
new file mode 100644
index 0000000..7590594
--- /dev/null
+++ b/res/values-v29/styles.xml
@@ -0,0 +1,34 @@
+<?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>
+    <!-- Launcher theme -->
+    <style name="BaseLauncherTheme" parent="@android:style/Theme.DeviceDefault.DayNight">
+        <item name="android:colorBackgroundCacheHint">@null</item>
+        <item name="android:colorEdgeEffect">#FF757575</item>
+        <item name="android:windowActionBar">false</item>
+        <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>
+    </style>
+</resources>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 06278f5..71decfc 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -33,12 +33,13 @@
     <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="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ữ để đặt theo cách thủ công"</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>
     <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>
@@ -64,7 +65,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Đây là ứng dụng hệ thống và không thể gỡ cài đặt."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Thư mục chưa đặt tên"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Đã vô hiệu hóa <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, có <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> thông báo</item>
       <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, có <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> thông báo</item>
     </plurals>
@@ -77,29 +78,22 @@
     <string name="folder_closed" msgid="4100806530910930934">"Đã đóng thư mục"</string>
     <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" msgid="6629239338071103179">"Thư mục: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Tiện ích con"</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>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Bị tắt bởi quản trị viên của bạn"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Cho phép xoay Màn hình chính"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Khi xoay điện thoại"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Dấu chấm thông báo"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Đang bật"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Đã tắt"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Dấu chấm thông báo"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Bật"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Tắt"</string>
     <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="icon_badging_service_title" msgid="2309733118428242174">"Hiển thị dấu chấm thông báo"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Hiển thị dấu chấm thông báo"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Thêm biểu tượ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="icon_shape_override_label" msgid="2977264953998281004">"Thay đổi hình dạng biểu tượng"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"trên Màn hình chính"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Sử dụng mặc định của hệ thống"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Hình vuông"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"Hình vuông cạnh bo tròn"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Hình tròn"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"Hình giọt nước"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Đang áp dụng các thay đổi hình dạng biểu tượng"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Không xác định"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Xóa"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Tìm kiếm"</string>
@@ -114,6 +108,7 @@
     <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="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>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Thông báo và ứng dụng đang tắt"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Đóng"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Đã đóng"</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 640aa0e..9804af1 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -33,12 +33,13 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"点按两次并按住微件即可选择微件,您也可以使用自定义操作。"</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="add_item_request_drag_hint" msgid="5899764264480397019">"轻触并按住即可手动放置"</string>
     <string name="place_automatically" msgid="8064208734425456485">"自动添加"</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>
@@ -64,7 +65,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"这是系统应用,无法卸载。"</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"未命名文件夹"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"已停用<xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>,有 <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> 个通知</item>
       <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>,有 <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> 个通知</item>
     </plurals>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"文件夹:<xliff:g id="NAME">%1$s</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="icon_badging_title" msgid="874121399231955394">"通知圆点"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"开启"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"关闭"</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="icon_badging_service_title" msgid="2309733118428242174">"显示通知圆点"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"显示通知圆点"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"将图标添加到主屏幕"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"适用于新应用"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"更改图标形状"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"在主屏幕上"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"使用系统默认设置"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"方形"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"方圆形"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"圆形"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"泪珠形"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"正在应用图标形状更改"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"未知"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"移除"</string>
     <string name="abandoned_search" msgid="891119232568284442">"搜索"</string>
@@ -114,6 +108,7 @@
     <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>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"通知和应用均已关闭"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"关闭"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"已关闭"</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 5a8c6ca..e737744 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -39,9 +39,10 @@
     <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_accessible_way_to_add_shortcut" msgid="3327314059613154633">"連㩒兩下之後繼續㩒住,就可以揀選捷徑或者用自訂嘅操作。"</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>
@@ -64,7 +65,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"這是系統應用程式,無法將其解除安裝。"</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"未命名的資料夾"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」已停用"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>,有 <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> 項通知</item>
       <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>,有 <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> 項通知</item>
     </plurals>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"資料夾:<xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"小工具"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"桌布"</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>
-    <string name="icon_badging_title" msgid="874121399231955394">"通知圓點"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"開啟"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"關閉"</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="icon_badging_service_title" msgid="2309733118428242174">"顯示通知圓點"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"顯示通知圓點"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"將圖示加到主畫面"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"適用於新安裝的應用程式"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"變更圖示形狀"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"在主畫面上"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"使用系統預設設定"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"正方形"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"方圓形"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"圓形"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"淚珠形"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"正在套用圖示形狀變更"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"不明"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"移除"</string>
     <string name="abandoned_search" msgid="891119232568284442">"搜尋"</string>
@@ -114,6 +108,7 @@
     <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>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"通知和應用程式已關閉"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"關閉"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"已關閉"</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 4d37cdd..e971b69 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -33,12 +33,13 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"輕觸兩下並按住小工具即可選取,你也可以使用自訂動作。"</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="add_item_request_drag_hint" msgid="5899764264480397019">"按住圖示即可手動新增"</string>
     <string name="place_automatically" msgid="8064208734425456485">"自動新增"</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>
@@ -64,7 +65,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"這是系統應用程式,不可解除安裝。"</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"未命名的資料夾"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"已停用 <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
       <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>,有 <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> 則通知</item>
       <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>,有 <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> 則通知</item>
     </plurals>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"資料夾:<xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"小工具"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"桌布"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Home 設定"</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="icon_badging_title" msgid="874121399231955394">"通知圓點"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"已啟用"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"已停用"</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="icon_badging_service_title" msgid="2309733118428242174">"顯示通知圓點"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"顯示通知圓點"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"將圖示加到主螢幕"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"適用於新安裝的應用程式"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"變更圖示形狀"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"在主螢幕上"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"使用系統預設值"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"正方形"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"方圓形"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"圓形"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"淚珠形"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"正在套用圖示形狀變更"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"不明"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"移除"</string>
     <string name="abandoned_search" msgid="891119232568284442">"搜尋"</string>
@@ -114,6 +108,7 @@
     <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>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"已關閉通知和應用程式"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"關閉"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"已關閉"</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 8e3e5ab..83178fe 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -39,6 +39,7 @@
     <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>
@@ -64,9 +65,9 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Lolu uhlelo lokusebenza lwesistimu futhi alikwazi ukukhishwa."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Ifolda engenagama"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"Kukhutshaziwe <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
-      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, inezaziso ezingu-<xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g></item>
-      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, inezaziso ezingu-<xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g></item>
+    <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
+      <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, unezaziso ezingu-<xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g></item>
+      <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, unezaziso ezingu-<xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g></item>
     </plurals>
     <string name="default_scroll_format" msgid="7475544710230993317">"Ikhasi elingu-%1$d kwangu-%2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Isikrini sasekhaya esingu-%1$d se-%2$d"</string>
@@ -79,27 +80,20 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Ifolda: <xliff:g id="NAME">%1$s</xliff:g>"</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>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Kukhutshazwe umlawuli wakho"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Vumela ukuphendukiswa kwesikrini sasekhaya"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Uma ifoni iphendukiswa"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Amachashazi esaziso"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Kuvuliwe"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Kuvaliwe"</string>
+    <string name="notification_dots_title" msgid="9062440428204120317">"Amacashazi esaziso"</string>
+    <string name="notification_dots_desc_on" msgid="1679848116452218908">"Vuliwe"</string>
+    <string name="notification_dots_desc_off" msgid="1760796511504341095">"Valiwe"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Ukufinyelela izaziso kuyadingeka"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Ukuze ubonisa amcashazi esaziso, vula izaziso zohlelo lokusebenza ze-<xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Shintsha izilungiselelo"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Bonisa amacashazi esaziso"</string>
+    <string name="notification_dots_service_title" msgid="4284221181793592871">"Bonisa amacashazi esaziso"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Engeza isithonjana eskrinini sasekhaya"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Kwezinhlelo zokusebenza ezintsha"</string>
-    <string name="icon_shape_override_label" msgid="2977264953998281004">"Shintsha isimo sesithonjana"</string>
-    <string name="icon_shape_override_label_location" msgid="3841607380657692863">"kusikrini sasekhaya"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Sebenzisa okuzenzakalelayo kwesistimu"</string>
-    <string name="icon_shape_square" msgid="633575066111622774">"Isikwele"</string>
-    <string name="icon_shape_squircle" msgid="5658049910802669495">"I-Squircle"</string>
-    <string name="icon_shape_circle" msgid="6550072265930144217">"Indingiliza"</string>
-    <string name="icon_shape_teardrop" msgid="4525869388200835463">"I-Teardrop"</string>
-    <string name="icon_shape_override_progress" msgid="3461735694970239908">"Ifaka izinguquko zesimo sesithonjana"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Akwaziwa"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Susa"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Sesha"</string>
@@ -114,6 +108,7 @@
     <string name="action_move_here" msgid="2170188780612570250">"Hambisa into lapha"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Into ingezwe kusikrini sasekhaya"</string>
     <string name="item_removed" msgid="851119963877842327">"Into isusiwe"</string>
+    <string name="undo" msgid="4151576204245173321">"Susa"</string>
     <string name="action_move" msgid="4339390619886385032">"Hambisa into"</string>
     <string name="move_to_empty_cell" msgid="2833711483015685619">"Hambisa kurowu engu-<xliff:g id="NUMBER_0">%1$s</xliff:g> ikholomu engu-<xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Hambisa kusimo esingu-<xliff:g id="NUMBER">%1$s</xliff:g>"</string>
@@ -144,4 +139,5 @@
     <string name="work_mode_off_label" msgid="3194894777601421047">"Izaziso nezinhlelo zokusebenza kuvaliwe"</string>
     <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Vala"</string>
     <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Kuvaliwe"</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 30091a5..69b8c8a 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -33,6 +33,13 @@
     <attr name="workspaceKeyShadowColor" format="color" />
     <attr name="workspaceStatusBarScrim" format="reference" />
     <attr name="widgetsTheme" format="reference" />
+    <attr name="loadingIconColor" format="color" />
+
+    <attr name="folderDotColor" format="color" />
+    <attr name="folderFillColor" format="color" />
+    <attr name="folderIconRadius" format="float" />
+    <attr name="folderIconBorderColor" format="color" />
+    <attr name="folderTextColor" format="color" />
 
     <!-- BubbleTextView specific attributes. -->
     <declare-styleable name="BubbleTextView">
@@ -48,6 +55,13 @@
         <attr name="centerVertically" format="boolean" />
     </declare-styleable>
 
+    <!-- BubbleTextView specific attributes. -->
+    <declare-styleable name="FolderIconPreview">
+        <attr name="folderFillColor" />
+        <attr name="folderIconBorderColor" />
+        <attr name="folderDotColor" />
+    </declare-styleable>
+
     <declare-styleable name="ShadowInfo">
         <attr name="ambientShadowColor" format="color" />
         <attr name="ambientShadowBlur" format="dimension" />
@@ -91,10 +105,8 @@
         <attr name="layout_ignoreInsets" format="boolean" />
     </declare-styleable>
 
-    <declare-styleable name="InvariantDeviceProfile">
+    <declare-styleable name="GridDisplayOption">
         <attr name="name" format="string" />
-        <attr name="minWidthDps" format="float" />
-        <attr name="minHeightDps" format="float" />
 
         <attr name="numRows" format="integer" />
         <attr name="numColumns" format="integer" />
@@ -104,13 +116,21 @@
         <!-- numHotseatIcons defaults to numColumns, if not specified -->
         <attr name="numHotseatIcons" format="integer" />
 
-        <attr name="iconSize" format="float" />
+        <attr name="defaultLayoutId" format="reference" />
+        <attr name="demoModeLayoutId" format="reference" />
+    </declare-styleable>
+
+    <declare-styleable name="ProfileDisplayOption">
+        <attr name="name" />
+        <attr name="minWidthDps" format="float" />
+        <attr name="minHeightDps" format="float" />
+
+        <attr name="iconImageSize" format="float" />
         <!-- landscapeIconSize defaults to iconSize, if not specified -->
         <attr name="landscapeIconSize" format="float" />
         <attr name="iconTextSize" format="float" />
-
-        <attr name="defaultLayoutId" format="reference" />
-        <attr name="demoModeLayoutId" format="reference" />
+        <!-- If true, this display option is used to determine the default grid -->
+        <attr name="canBeDefault" format="boolean" />
     </declare-styleable>
 
     <declare-styleable name="CellLayout">
@@ -146,4 +166,9 @@
         <attr name="numMinRows" format="integer" />
         <attr name="numMinColumns" format="integer" />
     </declare-styleable>
+
+    <declare-styleable name="PreviewFragment">
+        <attr name="android:name" />
+        <attr name="android:id" />
+    </declare-styleable>
 </resources>
diff --git a/res/values/bools.xml b/res/values/bools.xml
index 53c67e2..bc2c678 100644
--- a/res/values/bools.xml
+++ b/res/values/bools.xml
@@ -17,7 +17,7 @@
 -->
 
 <resources>
-    <bool name="notification_badging_enabled">false</bool>
+    <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 eb207af..3c8fe1e 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -34,7 +34,6 @@
     <color name="notification_icon_default_color">#757575</color> <!-- Gray 600 -->
 
     <color name="icon_background">#E0E0E0</color> <!-- Gray 300 -->
-    <color name="legacy_icon_background">#FFFFFF</color>
 
     <color name="all_apps_bg_hand_fill">#E5E5E5</color>
     <color name="all_apps_bg_hand_fill_dark">#9AA0A6</color>
diff --git a/res/values/config.xml b/res/values/config.xml
index f2d6c21..638a411 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -18,24 +18,8 @@
     <!-- String representing the intent to delete a package.-->
     <string name="delete_package_intent" translatable="false">#Intent;action=android.intent.action.DELETE;launchFlags=0x10800000;end</string>
 
-    <!-- Values for icon shape overrides. These should correspond to entries defined
-     in icon_shape_override_paths_names -->
-    <string-array translatable="false" name="icon_shape_override_paths_values">
-        <item></item>
-        <item>M50,0L100,0 100,100 0,100 0,0z</item>
-        <item>M50,0 C10,0 0,10 0,50 0,90 10,100 50,100 90,100 100,90 100,50 100,10 90,0 50,0 Z</item>
-        <item>M50 0A50 50,0,1,1,50 100A50 50,0,1,1,50 0</item>
-        <item>M50,0A50,50,0,0 1 100,50 L100,85 A15,15,0,0 1 85,100 L50,100 A50,50,0,0 1 50,0z</item>
-    </string-array>
-
-    <string-array translatable="false" name="icon_shape_override_paths_names">
-        <!-- Option to not change the icon shape on home screen. [CHAR LIMIT=50] -->
-        <item>@string/icon_shape_system_default</item>
-        <item>@string/icon_shape_square</item>
-        <item>@string/icon_shape_squircle</item>
-        <item>@string/icon_shape_circle</item>
-        <item>@string/icon_shape_teardrop</item>
-    </string-array>
+    <!-- String representing the fragment class for settings activity.-->
+    <string name="settings_fragment_name" translatable="false">com.android.launcher3.settings.SettingsActivity$LauncherSettingsFragment</string>
 
 <!-- DragController -->
     <item type="id" name="drag_event_parity" />
@@ -76,36 +60,24 @@
 <!-- Hotseat -->
     <bool name="hotseat_transpose_layout_with_orientation">true</bool>
 
-    <!-- Name of a subclass of com.android.launcher3.AppFilter used to
-         filter the activities shown in the launcher. Can be empty. -->
+    <!-- Various classes overriden by projects/build flavors. -->
     <string name="app_filter_class" translatable="false"></string>
-
-    <!-- Name of an icon provider class. -->
     <string name="icon_provider_class" translatable="false"></string>
-
-    <!-- Name of a drawable factory class. -->
     <string name="drawable_factory_class" translatable="false"></string>
-
-    <!-- Name of a user event dispatcher class. -->
     <string name="user_event_dispatcher_class" translatable="false"></string>
-
-    <!-- Name of an app transition manager class. -->
+    <string name="stats_log_manager_class" translatable="false"></string>
     <string name="app_transition_manager_class" translatable="false"></string>
-
-    <!-- Name of a color extraction implementation class. -->
-    <string name="color_extraction_impl_class" translatable="false"></string>
-
-    <!-- Name of a subclass of com.android.launcher3.util.InstantAppResolver. Can be empty. -->
     <string name="instant_app_resolver_class" translatable="false"></string>
-
-    <!-- Name of a main process initializer class. -->
     <string name="main_process_initializer_class" translatable="false"></string>
+    <string name="system_shortcut_factory_class" translatable="false"></string>
+    <string name="app_launch_tracker_class" translatable="false"></string>
+    <string name="test_information_handler_class" translatable="false"></string>
 
     <!-- Package name of the default wallpaper picker. -->
     <string name="wallpaper_picker_package" translatable="false"></string>
 
     <!-- Whitelisted package to retrieve packagename for badge. Can be empty. -->
-    <string name="shortcutinfocompat_badgepkg_whitelist" translatable="false"></string>
+    <string name="shortcutinfo_badgepkg_whitelist" translatable="false"></string>
 
     <!-- View ID to use for QSB widget -->
     <item type="id" name="qsb_widget" />
@@ -113,16 +85,15 @@
     <!-- View ID used by cell layout to jail its content -->
     <item type="id" name="cell_layout_jail_id" />
 
-    <!-- Tag id used for view scrim -->
-    <item type="id" name="view_scrim" />
-
     <!-- View IDs to store item highlight information -->
     <item type="id" name="view_unhighlight_background" />
-    <item type="id" name="view_highlighted" />
 
-<!-- Popup items -->
+    <!-- Menu id for feature flags -->
+    <item type="id" name="menu_apply_flags" />
+
+    <!-- Popup items -->
     <integer name="config_popupOpenCloseDuration">150</integer>
-    <integer name="config_popupArrowOpenDuration">80</integer>
+    <integer name="config_popupArrowOpenCloseDuration">40</integer>
     <integer name="config_removeNotificationViewDuration">300</integer>
 
 <!-- Accessibility actions -->
@@ -138,16 +109,12 @@
     <item type="id" name="action_deep_shortcuts" />
     <item type="id" name="action_shortcuts_and_notifications"/>
     <item type="id" name="action_dismiss_notification" />
+    <item type="id" name="action_remote_action_shortcut" />
 
 <!-- QSB IDs. DO not change -->
     <item type="id" name="search_container_workspace" />
-    <item type="id" name="search_container_hotseat" />
     <item type="id" name="search_container_all_apps" />
 
 <!-- Recents -->
     <item type="id" name="overview_panel"/>
-    <integer name="config_recentsMaxThumbnailCacheSize">6</integer>
-    <integer name="config_recentsMaxIconCacheSize">12</integer>
-
-    <item name="workspace_page_container" type="id" />
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 3bb7a79..4bcb8a7 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -22,13 +22,10 @@
     <dimen name="dynamic_grid_edge_margin">8dp</dimen>
     <dimen name="dynamic_grid_page_indicator_line_height">1dp</dimen>
     <dimen name="dynamic_grid_icon_drawable_padding">8dp</dimen>
-    <dimen name="dynamic_grid_workspace_top_padding">8dp</dimen>
-    <dimen name="dynamic_grid_workspace_page_spacing">8dp</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_layout_padding">5.5dp</dimen>
-    <dimen name="dynamic_grid_cell_layout_bottom_padding">0dp</dimen>
     <dimen name="dynamic_grid_cell_padding_x">8dp</dimen>
 
     <!-- Hotseat -->
@@ -36,12 +33,10 @@
     <dimen name="dynamic_grid_hotseat_bottom_padding">2dp</dimen>
     <!-- Extra bottom padding for non-tall devices. -->
     <dimen name="dynamic_grid_hotseat_bottom_non_tall_padding">0dp</dimen>
-    <dimen name="dynamic_grid_hotseat_size">80dp</dimen>
+    <dimen name="dynamic_grid_hotseat_extra_vertical_size">34dp</dimen>
     <dimen name="dynamic_grid_hotseat_side_padding">0dp</dimen>
 
     <!-- Hotseat/all-apps scrim -->
-    <dimen name="all_apps_scrim_radius">8dp</dimen>
-    <dimen name="all_apps_scrim_margin">8dp</dimen>
     <dimen name="all_apps_scrim_blur">4dp</dimen>
     <dimen name="vertical_drag_handle_size">24dp</dimen>
     <dimen name="vertical_drag_handle_overlap_workspace">0dp</dimen>
@@ -76,7 +71,6 @@
     <dimen name="fastscroll_end_margin">-26dp</dimen>
 
     <!-- All Apps -->
-    <dimen name="all_apps_button_scale_down">0dp</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>
@@ -226,4 +220,19 @@
 <!-- Overview -->
     <dimen name="options_menu_icon_size">24dp</dimen>
     <dimen name="options_menu_thumb_size">32dp</dimen>
+
+<!-- Snackbar -->
+    <dimen name="snackbar_height">48dp</dimen>
+    <dimen name="snackbar_content_height">32dp</dimen>
+    <dimen name="snackbar_padding">8dp</dimen>
+    <dimen name="snackbar_min_margin_left_right">6dp</dimen>
+    <dimen name="snackbar_max_margin_left_right">72dp</dimen>
+    <dimen name="snackbar_margin_bottom">30dp</dimen>
+    <dimen name="snackbar_elevation">3dp</dimen>
+    <dimen name="snackbar_min_text_size">12sp</dimen>
+    <dimen name="snackbar_max_text_size">14sp</dimen>
+
+<!-- Theming related -->
+    <dimen name="default_dialog_corner_radius">8dp</dimen>
+
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 7bc11c3..13e096c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -66,6 +66,8 @@
     <string name="all_apps_no_search_results">No apps found matching \"<xliff:g id="query" example="Android">%1$s</xliff:g>\"</string>
     <!-- Label for the button which allows the user to get app search results. [CHAR_LIMIT=50] -->
     <string name="all_apps_search_market_message">Search for more apps</string>
+    <!-- Label for an icon representing any generic app. [CHAR_LIMIT=50] -->
+    <string name="label_application">App</string>
 
     <!-- Popup items -->
     <!-- Text to display as the header above notifications. [CHAR_LIMIT=30] -->
@@ -92,9 +94,11 @@
          for accessibilty (spoken when the button gets focus). -->
     <string name="all_apps_home_button_label">Home</string>
 
-    <!-- Label for remove drop target. [CHAR_LIMIT=20] -->
+    <!-- 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>
-    <!-- Label for uninstall drop target. [CHAR_LIMIT=20]-->
+    <!-- Label for uninstall drop target (from the device).
+         May appear next to remove_drop_target_label [CHAR_LIMIT=20]-->
     <string name="uninstall_drop_target_label">Uninstall</string>
     <!-- Label for app info drop target. [CHAR_LIMIT=20] -->
     <string name="app_info_drop_target_label">App info</string>
@@ -141,7 +145,7 @@
     <!-- The format string for when an app is temporarily disabled. -->
     <string name="disabled_app_label">Disabled <xliff:g id="app_name" example="Messenger">%1$s</xliff:g></string>
     <!-- The format string for when an app has a notification dot (meaning it has associated notifications). -->
-    <plurals name="badged_app_label">
+    <plurals name="dotted_app_label">
         <item quantity="one"><xliff:g id="app_name" example="Messenger">%1$s</xliff:g>, has <xliff:g id="notification_count" example="1">%2$d</xliff:g> notification</item>
         <item quantity="other"><xliff:g id="app_name" example="Messenger">%1$s</xliff:g>, has <xliff:g id="notification_count" example="3">%2$d</xliff:g> notifications</item>
     </plurals>
@@ -173,6 +177,8 @@
     <string name="widget_button_text">Widgets</string>
     <!-- Text for wallpaper change button -->
     <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 -->
     <string name="settings_button_text">Home settings</string>
     <!-- Message shown when a feature is disabled by the administrator -->
@@ -184,11 +190,11 @@
     <!-- Text explaining when the home screen will get rotated. [CHAR LIMIT=100] -->
     <string name="allow_rotation_desc">When phone is rotated</string>
     <!-- Title for Notification dots setting. Tapping this will link to the system Notifications settings screen where the user can turn off notification dots globally. [CHAR LIMIT=50] -->
-    <string name="icon_badging_title">Notification dots</string>
-    <!-- Text to indicate that the system icon badging setting is on [CHAR LIMIT=100] -->
-    <string name="icon_badging_desc_on">On</string>
-    <!-- Text to indicate that the system icon badging setting is off [CHAR LIMIT=100] -->
-    <string name="icon_badging_desc_off">Off</string>
+    <string name="notification_dots_title">Notification dots</string>
+    <!-- Text to indicate that the system notification dots setting is on [CHAR LIMIT=100] -->
+    <string name="notification_dots_desc_on">On</string>
+    <!-- Text to indicate that the system notification dots setting is off [CHAR LIMIT=100] -->
+    <string name="notification_dots_desc_off">Off</string>
     <!-- Title for the dialog shown when the app does not has notification access, explaining the requirement for notification access [CHAR LIMIT=50] -->
     <string name="title_missing_notification_access">Notification access needed</string>
     <!-- Message explaining to the user that the notification access is required by the app for showing 'Notification dots' [CHAR LIMIT=NONE] -->
@@ -196,31 +202,13 @@
     <!-- Button text in the confirmation dialog which would take the user to the system settings [CHAR LIMIT=50] -->
     <string name="title_change_settings">Change settings</string>
     <!-- Summary for Notification dots setting. Tapping this will link enable/disable notification dots feature on the home screen. [CHAR LIMIT=50] -->
-    <string name="icon_badging_service_title">Show notification dots</string>
+    <string name="notification_dots_service_title">Show notification dots</string>
 
     <!-- Label for the setting that allows the automatic placement of launcher shortcuts for applications and games installed on the device [CHAR LIMIT=40] -->
     <string name="auto_add_shortcuts_label">Add icon to Home screen</string>
     <!-- Text description of the setting that allows the automatic placement of launcher shortcuts for applications and games installed on the device [CHAR LIMIT=NONE] -->
     <string name="auto_add_shortcuts_description">For new apps</string>
 
-    <!-- Developer setting to change the shape of icons on home screen. [CHAR LIMIT=50] -->
-    <string name="icon_shape_override_label">Change icon shape</string>
-    <!-- Subtext explaining that the icons will only be affected on the home screen. This text follows the actual icon action: Change icon shape, on Home screen [CHAR LIMIT=100] -->
-    <string name="icon_shape_override_label_location">on Home screen</string>
-    <!-- Option to not change the icon shape on home screen and use the system default setting instead. [CHAR LIMIT=50] -->
-    <string name="icon_shape_system_default">Use system default</string>
-    <!-- Option to change the shape of the home screen icons to a square. [CHAR LIMIT=50] -->
-    <string name="icon_shape_square">Square</string>
-    <!-- Option to change the shape of the home screen icons to a squircle. This represents the name of the shape somewhere between a circle and a square. [CHAR LIMIT=50] -->
-    <string name="icon_shape_squircle">Squircle</string>
-    <!-- Option to change the shape of the home screen icons to a circle. [CHAR LIMIT=50] -->
-    <string name="icon_shape_circle">Circle</string>
-    <!-- Option to change the shape of the home screen icons to a teardrop. This represents the name of the shape similar to a circle with with the bottom right corner pushed out like a square [CHAR LIMIT=50] -->
-    <string name="icon_shape_teardrop">Teardrop</string>
-
-    <!-- Message shown in the progress dialog when the icon shape override is being applied [CHAR LIMIT=100]-->
-    <string name="icon_shape_override_progress">Applying icon shape changes</string>
-
     <!-- Label on an icon that references an uninstalled package, for which we have no information about when it might be installed. [CHAR_LIMIT=15] -->
     <string name="package_state_unknown">Unknown</string>
 
@@ -259,9 +247,12 @@
     <!-- Accessibility confirmation for item added to workspace. -->
     <string name="item_added_to_workspace">Item added to home screen</string>
 
-    <!-- Accessibility confirmation for item removed. -->
+    <!-- Accessibility confirmation for item removed. [CHAR_LIMIT=50]-->
     <string name="item_removed">Item removed</string>
 
+    <!-- Action shown in snackbar to undo item removal. [CHAR_LIMIT=20] -->
+    <string name="undo">Undo</string>
+
     <!-- Accessibility action to move an item on the workspace. [CHAR_LIMIT=30] -->
     <string name="action_move">Move item</string>
 
@@ -344,4 +335,7 @@
     <string name="work_mode_off_label">Notifications and apps are off</string>
     <string name="bottom_work_tab_user_education_close_button">Close</string>
     <string name="bottom_work_tab_user_education_closed">Closed</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>
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 07bd800..881f65d 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -19,15 +19,17 @@
 
 <resources>
     <!-- Launcher theme -->
-    <style name="BaseLauncherTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
-        <item name="android:windowBackground">@android:color/transparent</item>
+    <style name="BaseLauncherTheme" parent="@android:style/Theme.DeviceDefault.Light">
         <item name="android:colorBackgroundCacheHint">@null</item>
-        <item name="android:windowShowWallpaper">true</item>
-        <item name="android:windowNoTitle">true</item>
         <item name="android:colorEdgeEffect">#FF757575</item>
+        <item name="android:windowActionBar">false</item>
+        <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="BaseLauncherThemeWithCustomAttrs" parent="@style/BaseLauncherTheme">
+    <style name="LauncherTheme" parent="@style/BaseLauncherTheme">
         <item name="allAppsScrimColor">#EAFFFFFF</item>
         <item name="allAppsInterimScrimAlpha">46</item>
         <item name="allAppsNavBarScrimColor">#66FFFFFF</item>
@@ -42,9 +44,23 @@
         <item name="workspaceKeyShadowColor">#44000000</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="folderIconBorderColor">?android:attr/colorPrimary</item>
+        <item name="folderTextColor">#FF212121</item>
+        <item name="loadingIconColor">#CCFFFFFF</item>
+
+        <item name="android:windowTranslucentStatus">false</item>
+        <item name="android:windowTranslucentNavigation">false</item>
+        <item name="android:windowDrawsSystemBarBackgrounds">true</item>
+        <item name="android:statusBarColor">#00000000</item>
+        <item name="android:navigationBarColor">#00000000</item>
     </style>
 
-    <style name="LauncherTheme" parent="@style/BaseLauncherThemeWithCustomAttrs"></style>
+    <style name="LauncherTheme.DarkMainColor" parent="@style/LauncherTheme">
+        <item name="folderFillColor">#FF3C4043</item> <!-- 100% GM2 800 -->
+        <item name="folderTextColor">?attr/workspaceTextColor</item>
+    </style>
 
     <style name="LauncherTheme.DarkText" parent="@style/LauncherTheme">
         <item name="workspaceTextColor">#FF212121</item>
@@ -54,6 +70,10 @@
         <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>
     </style>
 
     <style name="LauncherTheme.Dark" parent="@style/LauncherTheme">
@@ -66,15 +86,27 @@
         <item name="allAppsScrimColor">#EA212121</item>
         <item name="allAppsInterimScrimAlpha">102</item>
         <item name="allAppsNavBarScrimColor">#80000000</item>
-        <item name="popupColorPrimary">?android:attr/colorPrimary</item>
-        <item name="popupColorSecondary">#424242</item> <!-- Gray 800 -->
+        <item name="popupColorPrimary">#3C4043</item> <!-- Gray 800 -->
+        <item name="popupColorSecondary">#5F6368</item> <!-- Gray 700 -->
         <item name="popupColorTertiary">#757575</item> <!-- Gray 600 -->
         <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="isMainColorDark">true</item>
+        <item name="loadingIconColor">#99FFFFFF</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>
     </style>
 
     <style name="LauncherTheme.Dark.DarkText" parent="@style/LauncherTheme.Dark">
         <item name="allAppsInterimScrimAlpha">25</item>
+        <item name="folderFillColor">#CDFFFFFF</item>
+        <item name="folderTextColor">?attr/workspaceTextColor</item>
         <item name="workspaceTextColor">#FF212121</item>
         <item name="workspaceShadowColor">@android:color/transparent</item>
         <item name="workspaceAmbientShadowColor">@android:color/transparent</item>
@@ -86,10 +118,17 @@
     <!-- A derivative project can extend these themes to customize the application theme without
          affecting the base theme -->
     <style name="AppTheme" parent="@style/LauncherTheme" />
+    <style name="AppTheme.DarkMainColor" parent="@style/LauncherTheme.DarkMainColor" />
     <style name="AppTheme.DarkText" parent="@style/LauncherTheme.DarkText" />
+
     <style name="AppTheme.Dark" parent="@style/LauncherTheme.Dark" />
+    <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>
+
     <!--
     Theme overrides to element on homescreen, i.e., which are drawn on top on wallpaper.
     Various foreground colors are overridden to be workspaceTextColor so that they are properly
@@ -110,7 +149,7 @@
 
     <style name="WidgetContainerTheme.Dark" />
 
-    <style name="FastScrollerPopup" >
+    <style name="FastScrollerPopup" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle">
         <item name="android:layout_width">wrap_content</item>
         <item name="android:minWidth">@dimen/fastscroll_popup_width</item>
         <item name="android:layout_height">@dimen/fastscroll_popup_height</item>
@@ -126,23 +165,22 @@
     </style>
 
     <!-- Base theme for BubbleTextView and sub classes -->
-    <style name="BaseIcon">
+    <style name="BaseIcon" parent="@android:style/TextAppearance.DeviceDefault">
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">match_parent</item>
         <item name="android:layout_gravity">center</item>
         <item name="android:focusable">true</item>
         <item name="android:gravity">center_horizontal</item>
-        <item name="android:singleLine">true</item>
+        <item name="android:lines">1</item>
         <item name="android:textColor">?android:attr/textColorSecondary</item>
-        <item name="android:fontFamily">sans-serif-condensed</item>
         <item name="android:defaultFocusHighlightEnabled">false</item>
 
         <!-- No shadows in the base theme -->
         <item name="android:shadowRadius">0</item>
     </style>
 
-    <!-- Icon displayed on the worksapce -->
-    <style name="BaseIcon.Workspace">
+    <!-- Icon displayed on the workspace -->
+    <style name="BaseIcon.Workspace" >
         <item name="android:shadowRadius">2.0</item>
         <item name="android:shadowColor">?attr/workspaceShadowColor</item>
         <item name="ambientShadowColor">?attr/workspaceAmbientShadowColor</item>
@@ -158,7 +196,7 @@
     </style>
 
     <!-- Drop targets -->
-    <style name="DropTargetButtonBase">
+    <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>
@@ -174,9 +212,9 @@
 
     <style name="DropTargetButton" parent="DropTargetButtonBase" />
 
-    <style name="TextTitle">
-        <item name="android:fontFamily">sans-serif</item>
-    </style>
+    <style name="TextHeadline" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle" />
+
+    <style name="TextTitle" parent="@android:style/TextAppearance.DeviceDefault" />
 
     <style name="AllAppsEmptySearchBackground">
         <item name="android:colorPrimary">#E0E0E0</item>
diff --git a/res/xml/default_workspace_3x3.xml b/res/xml/default_workspace_3x3.xml
index 8d99404..31376e1 100644
--- a/res/xml/default_workspace_3x3.xml
+++ b/res/xml/default_workspace_3x3.xml
@@ -68,21 +68,4 @@
         <favorite launcher:uri="market://details?id=com.android.launcher" />
     </resolve>
 
-    <!-- Second last row -->
-    <resolve
-        launcher:screen="0"
-        launcher:x="0"
-        launcher:y="-2" >
-        <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_BROWSER;end" />
-        <favorite launcher:uri="http://www.example.com/" />
-    </resolve>
-
-    <resolve
-        launcher:screen="0"
-        launcher:x="2"
-        launcher:y="-2" >
-        <favorite launcher:uri="#Intent;action=android.media.action.STILL_IMAGE_CAMERA;end" />
-        <favorite launcher:uri="#Intent;action=android.intent.action.CAMERA_BUTTON;end" />
-    </resolve>
-
 </favorites>
diff --git a/res/xml/default_workspace_4x4.xml b/res/xml/default_workspace_4x4.xml
index 979a1b4..bf3c62c 100644
--- a/res/xml/default_workspace_4x4.xml
+++ b/res/xml/default_workspace_4x4.xml
@@ -16,8 +16,48 @@
 
 <favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3">
 
-    <!-- Hotseat -->
-    <include launcher:workspace="@xml/dw_phone_hotseat" />
+    <!-- Hotseat (We use the screen as the position of the item in the hotseat) -->
+    <!-- Dialer, Messaging, Browser, Camera -->
+    <resolve
+        launcher:container="-101"
+        launcher:screen="0"
+        launcher:x="0"
+        launcher:y="0" >
+        <favorite launcher:uri="#Intent;action=android.intent.action.DIAL;end" />
+        <favorite launcher:uri="tel:123" />
+        <favorite launcher:uri="#Intent;action=android.intent.action.CALL_BUTTON;end" />
+    </resolve>
+
+    <resolve
+        launcher:container="-101"
+        launcher:screen="1"
+        launcher:x="1"
+        launcher:y="0" >
+        <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MESSAGING;end" />
+        <favorite launcher:uri="sms:" />
+        <favorite launcher:uri="smsto:" />
+        <favorite launcher:uri="mms:" />
+        <favorite launcher:uri="mmsto:" />
+    </resolve>
+
+    <resolve
+        launcher:container="-101"
+        launcher:screen="2"
+        launcher:x="2"
+        launcher:y="0" >
+        <favorite
+            launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_BROWSER;end" />
+        <favorite launcher:uri="http://www.example.com/" />
+    </resolve>
+
+    <resolve
+        launcher:container="-101"
+        launcher:screen="3"
+        launcher:x="3"
+        launcher:y="0" >
+        <favorite launcher:uri="#Intent;action=android.media.action.STILL_IMAGE_CAMERA;end" />
+        <favorite launcher:uri="#Intent;action=android.intent.action.CAMERA_BUTTON;end" />
+    </resolve>
 
     <!-- Bottom row -->
     <resolve
diff --git a/res/xml/default_workspace_5x5.xml b/res/xml/default_workspace_5x5.xml
index f9cc0e7..ccdde2c 100644
--- a/res/xml/default_workspace_5x5.xml
+++ b/res/xml/default_workspace_5x5.xml
@@ -16,8 +16,57 @@
 
 <favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3">
 
-    <!-- Hotseat -->
-    <include launcher:workspace="@xml/dw_phone_hotseat" />
+    <!-- Hotseat (We use the screen as the position of the item in the hotseat) -->
+    <!-- Dialer, Messaging, [Maps/Music], Browser, Camera -->
+    <resolve
+        launcher:container="-101"
+        launcher:screen="0"
+        launcher:x="0"
+        launcher:y="0" >
+        <favorite launcher:uri="#Intent;action=android.intent.action.DIAL;end" />
+        <favorite launcher:uri="tel:123" />
+        <favorite launcher:uri="#Intent;action=android.intent.action.CALL_BUTTON;end" />
+    </resolve>
+
+    <resolve
+        launcher:container="-101"
+        launcher:screen="1"
+        launcher:x="1"
+        launcher:y="0" >
+        <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MESSAGING;end" />
+        <favorite launcher:uri="sms:" />
+        <favorite launcher:uri="smsto:" />
+        <favorite launcher:uri="mms:" />
+        <favorite launcher:uri="mmsto:" />
+    </resolve>
+
+    <resolve
+        launcher:container="-101"
+        launcher:screen="2"
+        launcher:x="2"
+        launcher:y="0" >
+        <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MAPS;end" />
+        <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MUSIC;end" />
+    </resolve>
+
+    <resolve
+        launcher:container="-101"
+        launcher:screen="3"
+        launcher:x="3"
+        launcher:y="0" >
+        <favorite
+            launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_BROWSER;end" />
+        <favorite launcher:uri="http://www.example.com/" />
+    </resolve>
+
+    <resolve
+        launcher:container="-101"
+        launcher:screen="4"
+        launcher:x="4"
+        launcher:y="0" >
+        <favorite launcher:uri="#Intent;action=android.media.action.STILL_IMAGE_CAMERA;end" />
+        <favorite launcher:uri="#Intent;action=android.intent.action.CAMERA_BUTTON;end" />
+    </resolve>
 
     <!-- Bottom row -->
     <resolve
diff --git a/res/xml/default_workspace_5x6.xml b/res/xml/default_workspace_5x6.xml
deleted file mode 100644
index 8493c26..0000000
--- a/res/xml/default_workspace_5x6.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3">
-
-    <!-- Hotseat -->
-    <include launcher:workspace="@xml/dw_tablet_hotseat" />
-
-    <!-- Bottom row -->
-    <favorite
-        launcher:screen="0"
-        launcher:x="0"
-        launcher:y="-1"
-        launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_CONTACTS;end" />
-
-    <resolve
-        launcher:screen="0"
-        launcher:x="-1"
-        launcher:y="-1" >
-        <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MARKET;end" />
-        <favorite launcher:uri="market://details?id=com.android.launcher" />
-    </resolve>
-
-</favorites>
diff --git a/res/xml/device_profiles.xml b/res/xml/device_profiles.xml
index ef6e145..82547d5 100644
--- a/res/xml/device_profiles.xml
+++ b/res/xml/device_profiles.xml
@@ -17,158 +17,109 @@
 
 <profiles xmlns:launcher="http://schemas.android.com/apk/res-auto" >
 
-    <profile
-        launcher:name="Super Short Stubby"
-        launcher:minWidthDps="255"
-        launcher:minHeightDps="300"
-        launcher:numRows="2"
+    <grid-option
+        launcher:name="3_by_3"
+        launcher:numRows="3"
         launcher:numColumns="3"
         launcher:numFolderRows="2"
         launcher:numFolderColumns="3"
-        launcher:iconSize="48"
-        launcher:iconTextSize="13.0"
         launcher:numHotseatIcons="3"
-        launcher:defaultLayoutId="@xml/default_workspace_3x3"
-        />
+        launcher:defaultLayoutId="@xml/default_workspace_3x3" >
 
-    <profile
-        launcher:name="Shorter Stubby"
-        launcher:minWidthDps="255"
-        launcher:minHeightDps="400"
-        launcher:numRows="3"
-        launcher:numColumns="3"
-        launcher:numFolderRows="3"
-        launcher:numFolderColumns="3"
-        launcher:iconSize="48"
-        launcher:iconTextSize="13.0"
-        launcher:numHotseatIcons="3"
-        launcher:defaultLayoutId="@xml/default_workspace_3x3"
-        />
+        <display-option
+            launcher:name="Super Short Stubby"
+            launcher:minWidthDps="255"
+            launcher:minHeightDps="300"
+            launcher:iconImageSize="48"
+            launcher:iconTextSize="13.0"
+            launcher:canBeDefault="true" />
 
-    <profile
-        launcher:name="Short Stubby"
-        launcher:minWidthDps="275"
-        launcher:minHeightDps="420"
-        launcher:numRows="3"
+        <display-option
+            launcher:name="Shorter Stubby"
+            launcher:minWidthDps="255"
+            launcher:minHeightDps="400"
+            launcher:iconImageSize="48"
+            launcher:iconTextSize="13.0"
+            launcher:canBeDefault="true" />
+
+    </grid-option>
+
+    <grid-option
+        launcher:name="4_by_4"
+        launcher:numRows="4"
         launcher:numColumns="4"
         launcher:numFolderRows="3"
         launcher:numFolderColumns="4"
-        launcher:iconSize="48"
-        launcher:iconTextSize="13.0"
-        launcher:numHotseatIcons="5"
-        launcher:defaultLayoutId="@xml/default_workspace_4x4"
-        />
+        launcher:numHotseatIcons="4"
+        launcher:defaultLayoutId="@xml/default_workspace_4x4" >
 
-    <profile
-        launcher:name="Stubby"
-        launcher:minWidthDps="255"
-        launcher:minHeightDps="450"
-        launcher:numRows="3"
-        launcher:numColumns="4"
-        launcher:numFolderRows="3"
-        launcher:numFolderColumns="4"
-        launcher:iconSize="48"
-        launcher:iconTextSize="13.0"
-        launcher:numHotseatIcons="5"
-        launcher:defaultLayoutId="@xml/default_workspace_4x4"
-        />
+        <display-option
+            launcher:name="Short Stubby"
+            launcher:minWidthDps="275"
+            launcher:minHeightDps="420"
+            launcher:iconImageSize="48"
+            launcher:iconTextSize="13.0"
+            launcher:canBeDefault="true" />
 
-    <profile
-        launcher:name="Nexus S"
-        launcher:minWidthDps="296"
-        launcher:minHeightDps="491.33"
-        launcher:numRows="4"
-        launcher:numColumns="4"
-        launcher:numFolderRows="4"
-        launcher:numFolderColumns="4"
-        launcher:iconSize="48"
-        launcher:iconTextSize="13.0"
-        launcher:numHotseatIcons="5"
-        launcher:defaultLayoutId="@xml/default_workspace_4x4"
-        />
+        <display-option
+            launcher:name="Stubby"
+            launcher:minWidthDps="255"
+            launcher:minHeightDps="450"
+            launcher:iconImageSize="48"
+            launcher:iconTextSize="13.0"
+            launcher:canBeDefault="true" />
 
-    <profile
-        launcher:name="Nexus 4"
-        launcher:minWidthDps="359"
-        launcher:minHeightDps="567"
-        launcher:numRows="4"
-        launcher:numColumns="4"
-        launcher:numFolderRows="4"
-        launcher:numFolderColumns="4"
-        launcher:iconSize="54"
-        launcher:iconTextSize="13.0"
-        launcher:numHotseatIcons="5"
-        launcher:defaultLayoutId="@xml/default_workspace_4x4"
-        />
+        <display-option
+            launcher:name="Nexus S"
+            launcher:minWidthDps="296"
+            launcher:minHeightDps="491.33"
+            launcher:iconImageSize="48"
+            launcher:iconTextSize="13.0"
+            launcher:canBeDefault="true" />
 
-    <profile
-        launcher:name="Nexus 5"
-        launcher:minWidthDps="335"
-        launcher:minHeightDps="567"
-        launcher:numRows="4"
-        launcher:numColumns="4"
-        launcher:numFolderRows="4"
-        launcher:numFolderColumns="4"
-        launcher:iconSize="54"
-        launcher:iconTextSize="13.0"
-        launcher:numHotseatIcons="5"
-        launcher:defaultLayoutId="@xml/default_workspace_4x4"
-        />
+        <display-option
+            launcher:name="Nexus 4"
+            launcher:minWidthDps="359"
+            launcher:minHeightDps="567"
+            launcher:iconImageSize="54"
+            launcher:iconTextSize="13.0"
+            launcher:canBeDefault="true" />
 
-    <profile
-        launcher:name="Large Phone"
-        launcher:minWidthDps="406"
-        launcher:minHeightDps="694"
+        <display-option
+            launcher:name="Nexus 5"
+            launcher:minWidthDps="335"
+            launcher:minHeightDps="567"
+            launcher:iconImageSize="54"
+            launcher:iconTextSize="13.0"
+            launcher:canBeDefault="true" />
+
+    </grid-option>
+
+    <grid-option
+        launcher:name="5_by_5"
         launcher:numRows="5"
         launcher:numColumns="5"
         launcher:numFolderRows="4"
         launcher:numFolderColumns="4"
-        launcher:iconSize="56"
-        launcher:iconTextSize="14.4"
         launcher:numHotseatIcons="5"
-        launcher:defaultLayoutId="@xml/default_workspace_5x5"
-        />
+        launcher:defaultLayoutId="@xml/default_workspace_5x5" >
 
-    <profile
-        launcher:name="Nexus 7"
-        launcher:minWidthDps="575"
-        launcher:minHeightDps="904"
-        launcher:numRows="6"
-        launcher:numColumns="6"
-        launcher:numFolderRows="4"
-        launcher:numFolderColumns="5"
-        launcher:iconSize="64"
-        launcher:iconTextSize="14.4"
-        launcher:numHotseatIcons="6"
-        launcher:defaultLayoutId="@xml/default_workspace_5x6"
-        />
+        <display-option
+            launcher:name="Large Phone"
+            launcher:minWidthDps="406"
+            launcher:minHeightDps="694"
+            launcher:iconImageSize="56"
+            launcher:iconTextSize="14.4"
+            launcher:canBeDefault="true" />
 
-    <profile
-        launcher:name="Nexus 10"
-        launcher:minWidthDps="727"
-        launcher:minHeightDps="1207"
-        launcher:numRows="6"
-        launcher:numColumns="7"
-        launcher:numFolderRows="4"
-        launcher:numFolderColumns="5"
-        launcher:iconSize="76"
-        launcher:iconTextSize="14.4"
-        launcher:numHotseatIcons="7"
-        launcher:defaultLayoutId="@xml/default_workspace_5x6"
-        />
+        <display-option
+            launcher:name="Shorter Stubby"
+            launcher:minWidthDps="255"
+            launcher:minHeightDps="400"
+            launcher:iconImageSize="48"
+            launcher:iconTextSize="13.0"
+            launcher:canBeDefault="true" />
 
-    <profile
-        launcher:name="20-inch Tablet"
-        launcher:minWidthDps="1527"
-        launcher:minHeightDps="2527"
-        launcher:numRows="7"
-        launcher:numColumns="7"
-        launcher:numFolderRows="6"
-        launcher:numFolderColumns="6"
-        launcher:iconSize="100"
-        launcher:iconTextSize="20.0"
-        launcher:numHotseatIcons="7"
-        launcher:defaultLayoutId="@xml/default_workspace_5x6"
-        />
+    </grid-option>
 
 </profiles>
\ No newline at end of file
diff --git a/res/xml/dw_phone_hotseat.xml b/res/xml/dw_phone_hotseat.xml
deleted file mode 100644
index b58994d..0000000
--- a/res/xml/dw_phone_hotseat.xml
+++ /dev/null
@@ -1,63 +0,0 @@
-<?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.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3">
-    <!-- Hotseat (We use the screen as the position of the item in the hotseat) -->
-    <!-- Dialer, Messaging, [All Apps], Browser, Camera -->
-    <resolve
-        launcher:container="-101"
-        launcher:screen="0"
-        launcher:x="0"
-        launcher:y="0" >
-        <favorite launcher:uri="#Intent;action=android.intent.action.DIAL;end" />
-        <favorite launcher:uri="tel:123" />
-        <favorite launcher:uri="#Intent;action=android.intent.action.CALL_BUTTON;end" />
-    </resolve>
-
-    <resolve
-        launcher:container="-101"
-        launcher:screen="1"
-        launcher:x="1"
-        launcher:y="0" >
-        <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MESSAGING;end" />
-        <favorite launcher:uri="sms:" />
-        <favorite launcher:uri="smsto:" />
-        <favorite launcher:uri="mms:" />
-        <favorite launcher:uri="mmsto:" />
-    </resolve>
-
-    <!-- All Apps -->
-
-    <resolve
-        launcher:container="-101"
-        launcher:screen="3"
-        launcher:x="3"
-        launcher:y="0" >
-        <favorite
-            launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_BROWSER;end" />
-        <favorite launcher:uri="http://www.example.com/" />
-    </resolve>
-
-    <resolve
-        launcher:container="-101"
-        launcher:screen="4"
-        launcher:x="4"
-        launcher:y="0" >
-        <favorite launcher:uri="#Intent;action=android.media.action.STILL_IMAGE_CAMERA;end" />
-        <favorite launcher:uri="#Intent;action=android.intent.action.CAMERA_BUTTON;end" />
-    </resolve>
-
-</favorites>
diff --git a/res/xml/dw_tablet_hotseat.xml b/res/xml/dw_tablet_hotseat.xml
deleted file mode 100644
index 671ccba..0000000
--- a/res/xml/dw_tablet_hotseat.xml
+++ /dev/null
@@ -1,78 +0,0 @@
-<?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.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3">
-    <!-- Hotseat (We use the screen as the position of the item in the hotseat) -->
-    <!-- Messaging, Email, Browser, [All Apps], Music, Gallery, Camera -->
-    <resolve
-        launcher:container="-101"
-        launcher:screen="0"
-        launcher:x="0"
-        launcher:y="0" >
-        <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MESSAGING;end" />
-        <favorite launcher:uri="sms:" />
-        <favorite launcher:uri="smsto:" />
-        <favorite launcher:uri="mms:" />
-        <favorite launcher:uri="mmsto:" />
-    </resolve>
-
-    <resolve
-        launcher:container="-101"
-        launcher:screen="1"
-        launcher:x="1"
-        launcher:y="0" >
-        <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_EMAIL;end" />
-        <favorite launcher:uri="mailto:" />
-    </resolve>
-
-    <resolve
-        launcher:container="-101"
-        launcher:screen="2"
-        launcher:x="2"
-        launcher:y="0" >
-        <favorite
-            launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_BROWSER;end" />
-        <favorite launcher:uri="http://www.example.com/" />
-    </resolve>
-
-    <!-- All Apps -->
-
-    <favorite
-        launcher:container="-101"
-        launcher:screen="4"
-        launcher:x="4"
-        launcher:y="0"
-        launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MUSIC;end" />
-
-    <resolve
-        launcher:container="-101"
-        launcher:screen="5"
-        launcher:x="5"
-        launcher:y="0" >
-        <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_GALLERY;end" />
-        <favorite launcher:uri="#Intent;type=images/*;end" />
-    </resolve>
-
-    <resolve
-        launcher:container="-101"
-        launcher:screen="6"
-        launcher:x="6"
-        launcher:y="0" >
-        <favorite launcher:uri="#Intent;action=android.media.action.STILL_IMAGE_CAMERA;end" />
-        <favorite launcher:uri="#Intent;action=android.intent.action.CAMERA_BUTTON;end" />
-    </resolve>
-
-</favorites>
diff --git a/res/xml/folder_shapes.xml b/res/xml/folder_shapes.xml
new file mode 100644
index 0000000..e60d333
--- /dev/null
+++ b/res/xml/folder_shapes.xml
@@ -0,0 +1,33 @@
+<?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.
+-->
+<shapes xmlns:launcher="http://schemas.android.com/apk/res-auto" >
+
+    <Circle launcher:folderIconRadius="1" />
+
+    <!-- Default icon for AOSP -->
+    <RoundedSquare launcher:folderIconRadius="0.16" />
+
+    <!-- Rounded icon from RRO -->
+    <RoundedSquare launcher:folderIconRadius="0.6" />
+
+    <!-- Square icon -->
+    <RoundedSquare launcher:folderIconRadius="0" />
+
+    <TearDrop launcher:folderIconRadius="0.3" />
+    <Squircle launcher:folderIconRadius="0.2" />
+
+</shapes>
\ No newline at end of file
diff --git a/res/xml/launcher_preferences.xml b/res/xml/launcher_preferences.xml
index 3bba73a..3455cb8 100644
--- a/res/xml/launcher_preferences.xml
+++ b/res/xml/launcher_preferences.xml
@@ -14,20 +14,21 @@
      limitations under the License.
 -->
 
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+<androidx.preference.PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android">
 
-    <com.android.launcher3.views.ButtonPreference
+    <com.android.launcher3.settings.NotificationDotsPreference
         android:key="pref_icon_badging"
-        android:title="@string/icon_badging_title"
+        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 icon badges" field in 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.views.ButtonPreference>
+    </com.android.launcher3.settings.NotificationDotsPreference>
 
     <SwitchPreference
         android:key="pref_add_icon_to_home"
@@ -43,13 +44,16 @@
         android:defaultValue="@bool/allow_rotation"
         android:persistent="true" />
 
-    <ListPreference
-        android:key="pref_override_icon_shape"
-        android:title="@string/icon_shape_override_label"
-        android:summary="%s"
-        android:entries="@array/icon_shape_override_paths_names"
-        android:entryValues="@array/icon_shape_override_paths_values"
-        android:defaultValue=""
-        android:persistent="false" />
+    <SwitchPreference
+        android:key="pref_grid_options"
+        android:title="Enable grid options"
+        android:defaultValue="false"
+        android:persistent="true" />
 
-</PreferenceScreen>
+    <androidx.preference.PreferenceScreen
+        android:key="pref_developer_options"
+        android:persistent="false"
+        android:title="Developer Options"
+        android:fragment="com.android.launcher3.settings.DeveloperOptionsFragment"/>
+
+</androidx.preference.PreferenceScreen>
diff --git a/robolectric_tests/Android.mk b/robolectric_tests/Android.mk
new file mode 100644
index 0000000..62915f2
--- /dev/null
+++ b/robolectric_tests/Android.mk
@@ -0,0 +1,57 @@
+# 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_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    androidx.test.runner \
+    androidx.test.rules \
+    mockito-robolectric-prebuilt \
+    truth-prebuilt
+LOCAL_JAVA_LIBRARIES := \
+    platform-robolectric-3.6.1-prebuilt
+
+LOCAL_JAVA_RESOURCE_DIRS := resources config
+
+LOCAL_INSTRUMENTATION_FOR := Launcher3
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+############################################
+# Target to run the previous target.       #
+############################################
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := RunLauncherRoboTests
+LOCAL_SDK_VERSION := current
+LOCAL_JAVA_LIBRARIES := \
+    LauncherRoboTests
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_TEST_PACKAGE := Launcher3
+
+LOCAL_INSTRUMENT_SOURCE_DIRS := $(dir $(LOCAL_PATH))../src \
+
+LOCAL_ROBOTEST_TIMEOUT := 36000
+
+include prebuilts/misc/common/robolectric/3.6.1/run_robotests.mk
diff --git a/robolectric_tests/config/robolectric.properties b/robolectric_tests/config/robolectric.properties
new file mode 100644
index 0000000..e0d6e53
--- /dev/null
+++ b/robolectric_tests/config/robolectric.properties
@@ -0,0 +1,2 @@
+manifest=packages/apps/Launcher3/AndroidManifest.xml
+sdk=26
diff --git a/robolectric_tests/res/values/overlayable_icons_test.xml b/robolectric_tests/res/values/overlayable_icons_test.xml
new file mode 100644
index 0000000..5144e52
--- /dev/null
+++ b/robolectric_tests/res/values/overlayable_icons_test.xml
@@ -0,0 +1,36 @@
+<!--
+   Copyright (C) 2019 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<resources>
+    <!-- overlayable_icons references all of the drawables in this package
+         that are being overlayed by resource overlays. If you remove/rename
+         any of these resources, you must also change the resource overlay icons.-->
+    <array name="overlayable_icons">
+        <item>@drawable/ic_corp</item>
+        <item>@drawable/ic_drag_handle</item>
+        <item>@drawable/ic_hourglass_top</item>
+        <item>@drawable/ic_info_no_shadow</item>
+        <item>@drawable/ic_install_no_shadow</item>
+        <item>@drawable/ic_palette</item>
+        <item>@drawable/ic_pin</item>
+        <item>@drawable/ic_remove_no_shadow</item>
+        <item>@drawable/ic_setting</item>
+        <item>@drawable/ic_smartspace_preferences</item>
+        <item>@drawable/ic_split_screen</item>
+        <item>@drawable/ic_uninstall_no_shadow</item>
+        <item>@drawable/ic_warning</item>
+        <item>@drawable/ic_widget</item>
+    </array>
+</resources>
diff --git a/robolectric_tests/resources/cache_data_updated_task_data.txt b/robolectric_tests/resources/cache_data_updated_task_data.txt
new file mode 100644
index 0000000..302d58f
--- /dev/null
+++ b/robolectric_tests/resources/cache_data_updated_task_data.txt
@@ -0,0 +1,28 @@
+# Model data used by CacheDataUpdatedTaskTest
+
+classMap s com.android.launcher3.WorkspaceItemInfo
+
+# 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
+
+# Auto install app shortcut
+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
+
+# Custom shortcuts
+bgItem s itemType=1 title=app1-shrt intent=component=app1/class3 id=7
+bgItem s itemType=1 title=app4-shrt intent=component=app4/class1 id=8
+
+# Restored custom shortcut
+bgItem s itemType=1 status=1 title=app3-shrt intent=component=app3/class3 id=9
+bgItem s itemType=1 status=1 title=app5-shrt intent=component=app5/class1 id=10
+
+allApps componentName=app1/class1 intent=component=app1/class1
+allApps componentName=app1/class2 intent=component=app1/class2
+allApps componentName=app2/class1 intent=component=app2/class1
+allApps componentName=app2/class2 intent=component=app2/class2
\ No newline at end of file
diff --git a/tests/res/raw/db_schema_v10.json b/robolectric_tests/resources/db_schema_v10.json
similarity index 100%
rename from tests/res/raw/db_schema_v10.json
rename to robolectric_tests/resources/db_schema_v10.json
diff --git a/robolectric_tests/resources/package_install_state_change_task_data.txt b/robolectric_tests/resources/package_install_state_change_task_data.txt
new file mode 100644
index 0000000..4d63664
--- /dev/null
+++ b/robolectric_tests/resources/package_install_state_change_task_data.txt
@@ -0,0 +1,24 @@
+# Model data used by PackageInstallStateChangeTaskTest
+
+classMap s com.android.launcher3.WorkspaceItemInfo
+classMap w com.android.launcher3.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/config/FlagOverrideRule.java b/robolectric_tests/src/com/android/launcher3/config/FlagOverrideRule.java
new file mode 100644
index 0000000..92bcc64
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/config/FlagOverrideRule.java
@@ -0,0 +1,116 @@
+package com.android.launcher3.config;
+
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+import org.robolectric.RuntimeEnvironment;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Test rule that makes overriding flags in Robolectric tests easier. This rule clears all flags
+ * before and after your test, avoiding one test method affecting subsequent methods.
+ *
+ * <p>Usage:
+ * <pre>
+ * {@literal @}Rule public final FlagOverrideRule flags = new FlagOverrideRule();
+ *
+ * {@literal @}FlagOverride(flag = "FOO", value=true)
+ * {@literal @}Test public void myTest() {
+ *     ...
+ * }
+ * </pre>
+ */
+public final class FlagOverrideRule implements TestRule {
+
+    /**
+     * Container annotation for handling multiple {@link FlagOverride} annotations.
+     * <p>
+     * <p>Don't use this directly, use repeated {@link FlagOverride} annotations instead.
+     */
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.METHOD})
+    public @interface FlagOverrides {
+        FlagOverride[] value();
+    }
+
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.METHOD})
+    @Repeatable(FlagOverrides.class)
+    public @interface FlagOverride {
+        String key();
+
+        boolean value();
+    }
+
+    private boolean ruleInProgress;
+
+    @Override
+    public Statement apply(Statement base, Description description) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                FeatureFlags.initialize(RuntimeEnvironment.application.getApplicationContext());
+                ruleInProgress = true;
+                try {
+                    clearOverrides();
+                    applyAnnotationOverrides(description);
+                    base.evaluate();
+                } finally {
+                    ruleInProgress = false;
+                    clearOverrides();
+                }
+            }
+        };
+    }
+
+    private void override(BaseFlags.TogglableFlag flag, boolean newValue) {
+        if (!ruleInProgress) {
+            throw new IllegalStateException(
+                    "Rule isn't in progress. Did you remember to mark it with @Rule?");
+        }
+        flag.setForTests(newValue);
+    }
+
+    private void applyAnnotationOverrides(Description description) {
+        for (Annotation annotation : description.getAnnotations()) {
+            if (annotation.annotationType() == FlagOverride.class) {
+                applyAnnotation((FlagOverride) annotation);
+            } else if (annotation.annotationType() == FlagOverrides.class) {
+                // Note: this branch is hit if the annotation is repeated
+                for (FlagOverride flagOverride : ((FlagOverrides) annotation).value()) {
+                    applyAnnotation(flagOverride);
+                }
+            }
+        }
+    }
+
+    private void applyAnnotation(FlagOverride flagOverride) {
+        boolean found = false;
+        for (BaseFlags.TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
+            if (flag.getKey().equals(flagOverride.key())) {
+                override(flag, flagOverride.value());
+                found = true;
+                break;
+            }
+        }
+        if (!found) {
+            throw new IllegalStateException("Flag " + flagOverride.key() + " not found");
+        }
+    }
+
+    /**
+     * Resets all flags to their default values.
+     */
+    private void clearOverrides() {
+        for (BaseFlags.TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
+            flag.setForTests(flag.getDefaultValue());
+        }
+    }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/config/FlagOverrideSampleTest.java b/robolectric_tests/src/com/android/launcher3/config/FlagOverrideSampleTest.java
new file mode 100644
index 0000000..1351348
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/config/FlagOverrideSampleTest.java
@@ -0,0 +1,36 @@
+package com.android.launcher3.config;
+
+import com.android.launcher3.config.FlagOverrideRule.FlagOverride;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Sample Robolectric test that demonstrates flag-overriding.
+ */
+@RunWith(RobolectricTestRunner.class)
+public class FlagOverrideSampleTest {
+
+    // Check out https://junit.org/junit4/javadoc/4.12/org/junit/Rule.html for more information
+    // on @Rules.
+    @Rule
+    public final FlagOverrideRule flags = new FlagOverrideRule();
+
+    @FlagOverride(key = "EXAMPLE_FLAG", value = true)
+    @Test
+    public void withFlagOn() {
+        assertTrue(FeatureFlags.EXAMPLE_FLAG.get());
+    }
+
+
+    @FlagOverride(key = "EXAMPLE_FLAG", value = false)
+    @Test
+    public void withFlagOff() {
+        assertFalse(FeatureFlags.EXAMPLE_FLAG.get());
+    }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java b/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java
new file mode 100644
index 0000000..096db57
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java
@@ -0,0 +1,91 @@
+package com.android.launcher3.logging;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Calendar;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for {@link FileLog}
+ */
+@RunWith(RobolectricTestRunner.class)
+public class FileLogTest {
+
+    private File mTempDir;
+
+    @Before
+    public void setUp() throws Exception {
+        int count = 0;
+        do {
+            mTempDir = new File(RuntimeEnvironment.application.getCacheDir(),
+                    "log-test-" + (count++));
+        } while (!mTempDir.mkdir());
+
+        FileLog.setDir(mTempDir);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        // Clear existing logs
+        new File(mTempDir, "log-0").delete();
+        new File(mTempDir, "log-1").delete();
+        mTempDir.delete();
+    }
+
+    @Test
+    public void testPrintLog() throws Exception {
+        if (!FileLog.ENABLED) {
+            return;
+        }
+        FileLog.print("Testing", "hoolalala");
+        StringWriter writer = new StringWriter();
+        FileLog.flushAll(new PrintWriter(writer));
+        assertTrue(writer.toString().contains("hoolalala"));
+
+        FileLog.print("Testing", "abracadabra", new Exception("cat! cat!"));
+        writer = new StringWriter();
+        FileLog.flushAll(new PrintWriter(writer));
+        assertTrue(writer.toString().contains("abracadabra"));
+        // Exception is also printed
+        assertTrue(writer.toString().contains("cat! cat!"));
+
+        // Old logs still present after flush
+        assertTrue(writer.toString().contains("hoolalala"));
+    }
+
+    @Test
+    public void testOldFileTruncated() throws Exception {
+        if (!FileLog.ENABLED) {
+            return;
+        }
+        FileLog.print("Testing", "hoolalala");
+        StringWriter writer = new StringWriter();
+        FileLog.flushAll(new PrintWriter(writer));
+        assertTrue(writer.toString().contains("hoolalala"));
+
+        Calendar threeDaysAgo = Calendar.getInstance();
+        threeDaysAgo.add(Calendar.HOUR, -72);
+        new File(mTempDir, "log-0").setLastModified(threeDaysAgo.getTimeInMillis());
+        new File(mTempDir, "log-1").setLastModified(threeDaysAgo.getTimeInMillis());
+
+        FileLog.print("Testing", "abracadabra", new Exception("cat! cat!"));
+        writer = new StringWriter();
+        FileLog.flushAll(new PrintWriter(writer));
+        assertTrue(writer.toString().contains("abracadabra"));
+        // Exception is also printed
+        assertTrue(writer.toString().contains("cat! cat!"));
+
+        // Old logs have been truncated
+        assertFalse(writer.toString().contains("hoolalala"));
+    }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
new file mode 100644
index 0000000..d7a2278
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
@@ -0,0 +1,176 @@
+package com.android.launcher3.model;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.verify;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.util.Pair;
+
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.util.ContentWriter;
+import com.android.launcher3.util.GridOccupancy;
+import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.IntSparseArrayMap;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * Tests for {@link AddWorkspaceItemsTask}
+ */
+@RunWith(RobolectricTestRunner.class)
+public class AddWorkspaceItemsTaskTest extends BaseModelUpdateTaskTestCase {
+
+    private final ComponentName mComponent1 = new ComponentName("a", "b");
+    private final ComponentName mComponent2 = new ComponentName("b", "b");
+
+    private IntArray existingScreens;
+    private IntArray newScreens;
+    private IntSparseArrayMap<GridOccupancy> screenOccupancy;
+
+    @Before
+    public void initData() throws Exception {
+        existingScreens = new IntArray();
+        screenOccupancy = new IntSparseArrayMap<>();
+        newScreens = new IntArray();
+
+        idp.numColumns = 5;
+        idp.numRows = 5;
+    }
+
+    private AddWorkspaceItemsTask newTask(ItemInfo... items) {
+        List<Pair<ItemInfo, Object>> list = new ArrayList<>();
+        for (ItemInfo item : items) {
+            list.add(Pair.create(item, null));
+        }
+        return new AddWorkspaceItemsTask(list);
+    }
+
+    @Test
+    public void testFindSpaceForItem_prefers_second() throws Exception {
+        // First screen has only one hole of size 1
+        int nextId = setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3));
+
+        // Second screen has 2 holes of sizes 3x2 and 2x3
+        setupWorkspaceWithHoles(nextId, 2, new Rect(2, 0, 5, 2), new Rect(0, 2, 2, 5));
+
+        int[] spaceFound = newTask()
+                .findSpaceForItem(appState, bgDataModel, existingScreens, newScreens, 1, 1);
+        assertEquals(2, spaceFound[0]);
+        assertTrue(screenOccupancy.get(spaceFound[0])
+                .isRegionVacant(spaceFound[1], spaceFound[2], 1, 1));
+
+        // Find a larger space
+        spaceFound = newTask()
+                .findSpaceForItem(appState, bgDataModel, existingScreens, newScreens, 2, 3);
+        assertEquals(2, spaceFound[0]);
+        assertTrue(screenOccupancy.get(spaceFound[0])
+                .isRegionVacant(spaceFound[1], spaceFound[2], 2, 3));
+    }
+
+    @Test
+    public void testFindSpaceForItem_adds_new_screen() throws Exception {
+        // First screen has 2 holes of sizes 3x2 and 2x3
+        setupWorkspaceWithHoles(1, 1, new Rect(2, 0, 5, 2), new Rect(0, 2, 2, 5));
+
+        IntArray oldScreens = existingScreens.clone();
+        int[] spaceFound = newTask()
+                .findSpaceForItem(appState, bgDataModel, existingScreens, newScreens, 3, 3);
+        assertFalse(oldScreens.contains(spaceFound[0]));
+        assertTrue(newScreens.contains(spaceFound[0]));
+    }
+
+    @Test
+    public void testAddItem_existing_item_ignored() throws Exception {
+        WorkspaceItemInfo info = new WorkspaceItemInfo();
+        info.intent = new Intent().setComponent(mComponent1);
+
+        // Setup a screen with a hole
+        setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3));
+
+        // Nothing was added
+        assertTrue(executeTaskForTest(newTask(info)).isEmpty());
+    }
+
+    @Test
+    public void testAddItem_some_items_added() throws Exception {
+        WorkspaceItemInfo info = new WorkspaceItemInfo();
+        info.intent = new Intent().setComponent(mComponent1);
+
+        WorkspaceItemInfo info2 = new WorkspaceItemInfo();
+        info2.intent = new Intent().setComponent(mComponent2);
+
+        // Setup a screen with a hole
+        setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3));
+
+        executeTaskForTest(newTask(info, info2)).get(0).run();
+        ArgumentCaptor<ArrayList> notAnimated = ArgumentCaptor.forClass(ArrayList.class);
+        ArgumentCaptor<ArrayList> animated = ArgumentCaptor.forClass(ArrayList.class);
+
+        // only info2 should be added because info was already added to the workspace
+        // in setupWorkspaceWithHoles()
+        verify(callbacks).bindAppsAdded(any(IntArray.class), notAnimated.capture(),
+                animated.capture());
+        assertTrue(notAnimated.getValue().isEmpty());
+
+        assertEquals(1, animated.getValue().size());
+        assertTrue(animated.getValue().contains(info2));
+    }
+
+    private int setupWorkspaceWithHoles(int startId, int screenId, Rect... holes) throws Exception {
+        GridOccupancy occupancy = new GridOccupancy(idp.numColumns, idp.numRows);
+        occupancy.markCells(0, 0, idp.numColumns, idp.numRows, true);
+        for (Rect r : holes) {
+            occupancy.markCells(r, false);
+        }
+
+        existingScreens.add(screenId);
+        screenOccupancy.append(screenId, occupancy);
+
+        ExecutorService executor = Executors.newSingleThreadExecutor();
+        for (int x = 0; x < idp.numColumns; x++) {
+            for (int y = 0; y < idp.numRows; y++) {
+                if (!occupancy.cells[x][y]) {
+                    continue;
+                }
+
+                WorkspaceItemInfo info = new WorkspaceItemInfo();
+                info.intent = new Intent().setComponent(mComponent1);
+                info.id = startId++;
+                info.screenId = screenId;
+                info.cellX = x;
+                info.cellY = y;
+                info.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
+                bgDataModel.addItem(targetContext, info, false);
+
+                executor.execute(() -> {
+                    ContentWriter writer = new ContentWriter(targetContext);
+                    info.writeToValues(writer);
+                    writer.put(Favorites._ID, info.id);
+                    targetContext.getContentResolver().insert(Favorites.CONTENT_URI,
+                            writer.getValues(targetContext));
+                });
+            }
+        }
+
+        executor.submit(() -> null).get();
+        executor.shutdown();
+        return startId;
+    }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/model/BaseGridChangesTestCase.java b/robolectric_tests/src/com/android/launcher3/model/BaseGridChangesTestCase.java
new file mode 100644
index 0000000..07834fc
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/model/BaseGridChangesTestCase.java
@@ -0,0 +1,121 @@
+package com.android.launcher3.model;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.database.sqlite.SQLiteDatabase;
+
+import com.android.launcher3.LauncherProvider;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.util.TestLauncherProvider;
+
+import org.junit.Before;
+import org.robolectric.Robolectric;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadows.ShadowContentResolver;
+import org.robolectric.shadows.ShadowLog;
+
+public abstract class BaseGridChangesTestCase {
+
+
+    public static final int DESKTOP = LauncherSettings.Favorites.CONTAINER_DESKTOP;
+    public static final int HOTSEAT = LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+
+    public static final int APP_ICON = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+    public static final int SHORTCUT = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
+    public static final int NO__ICON = -1;
+
+    public static final String TEST_PACKAGE = "com.android.launcher3.validpackage";
+
+    public Context mContext;
+    public TestLauncherProvider mProvider;
+    public SQLiteDatabase mDb;
+
+    @Before
+    public void setUpBaseCase() {
+        ShadowLog.stream = System.out;
+
+        mContext = RuntimeEnvironment.application;
+        mProvider = Robolectric.setupContentProvider(TestLauncherProvider.class);
+        ShadowContentResolver.registerProviderInternal(LauncherProvider.AUTHORITY, mProvider);
+        mDb = mProvider.getDb();
+    }
+
+    /**
+     * Adds a dummy 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).
+     */
+    public int addItem(int type, int screen, int container, int x, int y) {
+        int id = LauncherSettings.Settings.call(mContext.getContentResolver(),
+                LauncherSettings.Settings.METHOD_NEW_ITEM_ID)
+                .getInt(LauncherSettings.Settings.EXTRA_VALUE);
+
+        ContentValues values = new ContentValues();
+        values.put(LauncherSettings.Favorites._ID, id);
+        values.put(LauncherSettings.Favorites.CONTAINER, container);
+        values.put(LauncherSettings.Favorites.SCREEN, screen);
+        values.put(LauncherSettings.Favorites.CELLX, x);
+        values.put(LauncherSettings.Favorites.CELLY, y);
+        values.put(LauncherSettings.Favorites.SPANX, 1);
+        values.put(LauncherSettings.Favorites.SPANY, 1);
+
+        if (type == APP_ICON || type == SHORTCUT) {
+            values.put(LauncherSettings.Favorites.ITEM_TYPE, type);
+            values.put(LauncherSettings.Favorites.INTENT,
+                    new Intent(Intent.ACTION_MAIN).setPackage(TEST_PACKAGE).toUri(0));
+        } else {
+            values.put(LauncherSettings.Favorites.ITEM_TYPE,
+                    LauncherSettings.Favorites.ITEM_TYPE_FOLDER);
+            // Add folder items.
+            for (int i = 0; i < type; i++) {
+                addItem(APP_ICON, 0, id, 0, 0);
+            }
+        }
+
+        mContext.getContentResolver().insert(LauncherSettings.Favorites.CONTENT_URI, values);
+        return id;
+    }
+
+    public int[][][] createGrid(int[][][] typeArray) {
+        return createGrid(typeArray, 1);
+    }
+
+    /**
+     * Initializes the DB with dummy 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.
+     * @param startScreen First screen id from where the icons will be added.
+     * @return the same grid representation where each entry is the corresponding item id.
+     */
+    public int[][][] createGrid(int[][][] typeArray, int startScreen) {
+        LauncherSettings.Settings.call(mContext.getContentResolver(),
+                LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
+        int[][][] ids = new int[typeArray.length][][];
+
+        for (int i = 0; i < typeArray.length; i++) {
+            // Add screen to DB
+            int screenId = startScreen + i;
+
+            // Keep the screen id counter up to date
+            LauncherSettings.Settings.call(mContext.getContentResolver(),
+                    LauncherSettings.Settings.METHOD_NEW_SCREEN_ID);
+
+            ids[i] = new int[typeArray[i].length][];
+            for (int y = 0; y < typeArray[i].length; y++) {
+                ids[i][y] = new int[typeArray[i][y].length];
+                for (int x = 0; x < typeArray[i][y].length; x++) {
+                    if (typeArray[i][y][x] < 0) {
+                        // Empty cell
+                        ids[i][y][x] = -1;
+                    } else {
+                        ids[i][y][x] = addItem(typeArray[i][y][x], screenId, DESKTOP, x, y);
+                    }
+                }
+            }
+        }
+
+        return ids;
+    }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java b/robolectric_tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
new file mode 100644
index 0000000..ab39274
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
@@ -0,0 +1,221 @@
+package com.android.launcher3.model;
+
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Color;
+import android.os.Process;
+import android.os.UserHandle;
+
+import com.android.launcher3.AllAppsList;
+import com.android.launcher3.AppFilter;
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.LauncherModel.Callbacks;
+import com.android.launcher3.LauncherModel.ModelUpdateTask;
+import com.android.launcher3.LauncherProvider;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.icons.cache.CachingLogic;
+import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.TestLauncherProvider;
+
+import org.junit.Before;
+import org.mockito.ArgumentCaptor;
+import org.robolectric.Robolectric;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadows.ShadowContentResolver;
+import org.robolectric.shadows.ShadowLog;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.function.Supplier;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Base class for writing tests for Model update tasks.
+ */
+public class BaseModelUpdateTaskTestCase {
+
+    public final HashMap<Class, HashMap<String, Field>> fieldCache = new HashMap<>();
+    private TestLauncherProvider mProvider;
+
+    public Context targetContext;
+    public UserHandle myUser;
+
+    public InvariantDeviceProfile idp;
+    public LauncherAppState appState;
+    public LauncherModel model;
+    public ModelWriter modelWriter;
+    public MyIconCache iconCache;
+
+    public BgDataModel bgDataModel;
+    public AllAppsList allAppsList;
+    public Callbacks callbacks;
+
+    @Before
+    public void setUp() throws Exception {
+        ShadowLog.stream = System.out;
+
+        mProvider = Robolectric.setupContentProvider(TestLauncherProvider.class);
+        ShadowContentResolver.registerProviderInternal(LauncherProvider.AUTHORITY, mProvider);
+
+        callbacks = mock(Callbacks.class);
+        appState = mock(LauncherAppState.class);
+        model = mock(LauncherModel.class);
+        modelWriter = mock(ModelWriter.class);
+
+        when(appState.getModel()).thenReturn(model);
+        when(model.getWriter(anyBoolean(), anyBoolean())).thenReturn(modelWriter);
+        when(model.getCallback()).thenReturn(callbacks);
+
+        myUser = Process.myUserHandle();
+
+        bgDataModel = new BgDataModel();
+        targetContext = RuntimeEnvironment.application;
+
+        idp = new InvariantDeviceProfile();
+        iconCache = new MyIconCache(targetContext, idp);
+
+        allAppsList = new AllAppsList(iconCache, new AppFilter());
+
+        when(appState.getIconCache()).thenReturn(iconCache);
+        when(appState.getInvariantDeviceProfile()).thenReturn(idp);
+        when(appState.getContext()).thenReturn(targetContext);
+    }
+
+    /**
+     * Synchronously executes the task and returns all the UI callbacks posted.
+     */
+    public List<Runnable> executeTaskForTest(ModelUpdateTask task) throws Exception {
+        when(model.isModelLoaded()).thenReturn(true);
+
+        Executor mockExecutor = mock(Executor.class);
+
+        task.init(appState, model, bgDataModel, allAppsList, mockExecutor);
+        task.run();
+        ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
+        verify(mockExecutor, atLeast(0)).execute(captor.capture());
+
+        return captor.getAllValues();
+    }
+
+    /**
+     * Initializes mock data for the test.
+     */
+    public void initializeData(String resourceName) throws Exception {
+        try (BufferedReader reader = new BufferedReader(new InputStreamReader(
+                this.getClass().getResourceAsStream(resourceName)))) {
+            String line;
+            HashMap<String, Class> classMap = new HashMap<>();
+            while((line = reader.readLine()) != null) {
+                line = line.trim();
+                if (line.startsWith("#") || line.isEmpty()) {
+                    continue;
+                }
+                String[] commands = line.split(" ");
+                switch (commands[0]) {
+                    case "classMap":
+                        classMap.put(commands[1], Class.forName(commands[2]));
+                        break;
+                    case "bgItem":
+                        bgDataModel.addItem(targetContext,
+                                (ItemInfo) initItem(classMap.get(commands[1]), commands, 2), false);
+                        break;
+                    case "allApps":
+                        allAppsList.add((AppInfo) initItem(AppInfo.class, commands, 1), null);
+                        break;
+                }
+            }
+        }
+    }
+
+    private Object initItem(Class clazz, String[] fieldDef, int startIndex) throws Exception {
+        HashMap<String, Field> cache = fieldCache.get(clazz);
+        if (cache == null) {
+            cache = new HashMap<>();
+            Class c = clazz;
+            while (c != null) {
+                for (Field f : c.getDeclaredFields()) {
+                    f.setAccessible(true);
+                    cache.put(f.getName(), f);
+                }
+                c = c.getSuperclass();
+            }
+            fieldCache.put(clazz, cache);
+        }
+
+        Object item = clazz.newInstance();
+        for (int i = startIndex; i < fieldDef.length; i++) {
+            String[] fieldData = fieldDef[i].split("=", 2);
+            Field f = cache.get(fieldData[0]);
+            Class type = f.getType();
+            if (type == int.class || type == long.class) {
+                f.set(item, Integer.parseInt(fieldData[1]));
+            } else if (type == CharSequence.class || type == String.class) {
+                f.set(item, fieldData[1]);
+            } else if (type == Intent.class) {
+                if (!fieldData[1].startsWith("#Intent")) {
+                    fieldData[1] = "#Intent;" + fieldData[1] + ";end";
+                }
+                f.set(item, Intent.parseUri(fieldData[1], 0));
+            } else if (type == ComponentName.class) {
+                f.set(item, ComponentName.unflattenFromString(fieldData[1]));
+            } else {
+                throw new Exception("Added parsing logic for "
+                        + f.getName() + " of type " + f.getType());
+            }
+        }
+        return item;
+    }
+
+    public static class MyIconCache extends IconCache {
+
+        private final HashMap<ComponentKey, CacheEntry> mCache = new HashMap<>();
+
+        public MyIconCache(Context context, InvariantDeviceProfile idp) {
+            super(context, idp);
+        }
+
+        @Override
+        protected <T> CacheEntry cacheLocked(
+                @NonNull ComponentName componentName,
+                UserHandle user, @NonNull Supplier<T> infoProvider,
+                @NonNull CachingLogic<T> cachingLogic,
+                boolean usePackageIcon, boolean useLowResIcon) {
+            CacheEntry entry = mCache.get(new ComponentKey(componentName, user));
+            if (entry == null) {
+                entry = new CacheEntry();
+                getDefaultIcon(user).applyTo(entry);
+            }
+            return entry;
+        }
+
+        public void addCache(ComponentName key, String title) {
+            CacheEntry entry = new CacheEntry();
+            entry.icon = newIcon();
+            entry.color = Color.RED;
+            entry.title = title;
+            mCache.put(new ComponentKey(key, Process.myUserHandle()), entry);
+        }
+
+        public Bitmap newIcon() {
+            return Bitmap.createBitmap(1, 1, Config.ARGB_8888);
+        }
+    }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
new file mode 100644
index 0000000..42848f4
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
@@ -0,0 +1,99 @@
+package com.android.launcher3.model;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.WorkspaceItemInfo;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.Arrays;
+import java.util.HashSet;
+
+/**
+ * Tests for {@link CacheDataUpdatedTask}
+ */
+@RunWith(RobolectricTestRunner.class)
+public class CacheDataUpdatedTaskTest extends BaseModelUpdateTaskTestCase {
+
+    private static final String NEW_LABEL_PREFIX = "new-label-";
+
+    @Before
+    public void initData() throws Exception {
+        initializeData("/cache_data_updated_task_data.txt");
+        // Add dummy entries in the cache to simulate update
+        for (ItemInfo info : bgDataModel.itemsIdMap) {
+            iconCache.addCache(info.getTargetComponent(), NEW_LABEL_PREFIX + info.id);
+        }
+    }
+
+    private CacheDataUpdatedTask newTask(int op, String... pkg) {
+        return new CacheDataUpdatedTask(op, myUser, new HashSet<>(Arrays.asList(pkg)));
+    }
+
+    @Test
+    @Ignore("This test fails with resource errors") // b/131115553
+    public void testCacheUpdate_update_apps() throws Exception {
+        // Clear all icons from apps list so that its easy to check what was updated
+        for (AppInfo info : allAppsList.data) {
+            info.iconBitmap = null;
+        }
+
+        executeTaskForTest(newTask(CacheDataUpdatedTask.OP_CACHE_UPDATE, "app1"));
+
+        // Verify that only the app icons of app1 (id 1 & 2) are updated. Custom shortcut (id 7)
+        // is not updated
+        verifyUpdate(1, 2);
+
+        // Verify that only app1 var updated in allAppsList
+        assertFalse(allAppsList.data.isEmpty());
+        for (AppInfo info : allAppsList.data) {
+            if (info.componentName.getPackageName().equals("app1")) {
+                assertNotNull(info.iconBitmap);
+            } else {
+                assertNull(info.iconBitmap);
+            }
+        }
+    }
+
+    @Test
+    @Ignore("This test fails with resource errors") // b/131115553
+    public void testSessionUpdate_ignores_normal_apps() throws Exception {
+        executeTaskForTest(newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, "app1"));
+
+        // app1 has no restored shortcuts. Verify that nothing was updated.
+        verifyUpdate();
+    }
+
+    @Test
+    @Ignore("This test fails with resource errors") // b/131115553
+    public void testSessionUpdate_updates_pending_apps() throws Exception {
+        executeTaskForTest(newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, "app3"));
+
+        // app3 has only restored apps (id 5, 6) and shortcuts (id 9). Verify that only apps were
+        // were updated
+        verifyUpdate(5, 6);
+    }
+
+    private void verifyUpdate(Integer... idsUpdated) {
+        HashSet<Integer> updates = new HashSet<>(Arrays.asList(idsUpdated));
+        for (ItemInfo info : bgDataModel.itemsIdMap) {
+            if (updates.contains(info.id)) {
+                assertEquals(NEW_LABEL_PREFIX + info.id, info.title);
+                assertNotNull(((WorkspaceItemInfo) info).iconBitmap);
+            } else {
+                assertNotSame(NEW_LABEL_PREFIX + info.id, info.title);
+                assertNull(((WorkspaceItemInfo) info).iconBitmap);
+            }
+        }
+    }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java b/robolectric_tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
new file mode 100644
index 0000000..a46617e
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
@@ -0,0 +1,209 @@
+/*
+ * 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;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotSame;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+
+import com.android.launcher3.LauncherProvider;
+import com.android.launcher3.LauncherProvider.DatabaseHelper;
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.io.File;
+
+/**
+ * Tests for {@link DbDowngradeHelper}
+ */
+@RunWith(RobolectricTestRunner.class)
+public class DbDowngradeHelperTest {
+
+    private static final String SCHEMA_FILE = "test_schema.json";
+    private static final String DB_FILE = "test.db";
+
+    private Context mContext;
+    private File mSchemaFile;
+    private File mDbFile;
+
+    @Before
+    public void setup() {
+        mContext = RuntimeEnvironment.application;
+        mSchemaFile = mContext.getFileStreamPath(SCHEMA_FILE);
+        mDbFile = mContext.getDatabasePath(DB_FILE);
+    }
+
+    @Test
+    public void testDowngradeSchemaMatchesVersion() throws Exception {
+        mSchemaFile.delete();
+        assertFalse(mSchemaFile.exists());
+        DbDowngradeHelper.updateSchemaFile(mSchemaFile, 0, mContext);
+        assertEquals(LauncherProvider.SCHEMA_VERSION, DbDowngradeHelper.parse(mSchemaFile).version);
+    }
+
+    @Test
+    public void testUpdateSchemaFile() throws Exception {
+        // Setup mock resources
+        Resources res = spy(mContext.getResources());
+        doAnswer(i ->this.getClass().getResourceAsStream("/db_schema_v10.json"))
+                .when(res).openRawResource(eq(R.raw.downgrade_schema));
+        Context context = spy(mContext);
+        when(context.getResources()).thenReturn(res);
+
+        mSchemaFile.delete();
+        assertFalse(mSchemaFile.exists());
+
+        DbDowngradeHelper.updateSchemaFile(mSchemaFile, 10, context);
+        assertTrue(mSchemaFile.exists());
+        assertEquals(10, DbDowngradeHelper.parse(mSchemaFile).version);
+
+        // Schema is updated on version upgrade
+        assertTrue(mSchemaFile.setLastModified(0));
+        DbDowngradeHelper.updateSchemaFile(mSchemaFile, 11, context);
+        assertNotSame(0, mSchemaFile.lastModified());
+
+        // Schema is not updated when version is same
+        assertTrue(mSchemaFile.setLastModified(0));
+        DbDowngradeHelper.updateSchemaFile(mSchemaFile, 10, context);
+        assertEquals(0, mSchemaFile.lastModified());
+
+        // Schema is not updated on version downgrade
+        DbDowngradeHelper.updateSchemaFile(mSchemaFile, 3, context);
+        assertEquals(0, mSchemaFile.lastModified());
+    }
+
+    @Test
+    public void testDowngrade_success_v24() throws Exception {
+        setupTestDb();
+
+        TestOpenHelper helper = new TestOpenHelper(24);
+        assertEquals(24, helper.getReadableDatabase().getVersion());
+        helper.close();
+    }
+
+    @Test
+    public void testDowngrade_success_v22() throws Exception {
+        setupTestDb();
+
+        SQLiteOpenHelper helper = new TestOpenHelper(22);
+        assertEquals(22, helper.getWritableDatabase().getVersion());
+
+        // Check column does not exist
+        try (Cursor c = helper.getWritableDatabase().query(Favorites.TABLE_NAME,
+                null, null, null, null, null, null)) {
+            assertEquals(-1, c.getColumnIndex(Favorites.OPTIONS));
+
+            // Check data is present
+            assertEquals(10, c.getCount());
+        }
+        helper.close();
+
+        helper = new DatabaseHelper(mContext, null, DB_FILE) {
+            @Override
+            public void onOpen(SQLiteDatabase db) { }
+        };
+        assertEquals(LauncherProvider.SCHEMA_VERSION, helper.getWritableDatabase().getVersion());
+
+        try (Cursor c = helper.getWritableDatabase().query(Favorites.TABLE_NAME,
+                null, null, null, null, null, null)) {
+            // Check column exists
+            assertNotSame(-1, c.getColumnIndex(Favorites.OPTIONS));
+
+            // Check data is present
+            assertEquals(10, c.getCount());
+        }
+        helper.close();
+    }
+
+    @Test(expected = DowngradeFailException.class)
+    public void testDowngrade_fail_v20() throws Exception {
+        setupTestDb();
+
+        TestOpenHelper helper = new TestOpenHelper(20);
+        helper.getReadableDatabase().getVersion();
+    }
+
+    private void setupTestDb() throws Exception {
+        mSchemaFile.delete();
+        mDbFile.delete();
+
+        DbDowngradeHelper.updateSchemaFile(mSchemaFile, LauncherProvider.SCHEMA_VERSION, mContext);
+
+        DatabaseHelper dbHelper = new DatabaseHelper(mContext, null, DB_FILE) {
+            @Override
+            public void onOpen(SQLiteDatabase db) { }
+        };
+        // Insert dummy data
+        for (int i = 0; i < 10; i++) {
+            ContentValues values = new ContentValues();
+            values.put(Favorites._ID, i);
+            values.put(Favorites.TITLE, "title " + i);
+            dbHelper.getWritableDatabase().insert(Favorites.TABLE_NAME, null, values);
+        }
+        dbHelper.close();
+    }
+
+    private class TestOpenHelper extends SQLiteOpenHelper {
+
+        public TestOpenHelper(int version) {
+            super(mContext, DB_FILE, null, version);
+        }
+
+        @Override
+        public void onCreate(SQLiteDatabase sqLiteDatabase) {
+            throw new RuntimeException("DB should already be created");
+        }
+
+        @Override
+        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+            throw new RuntimeException("Only downgrade supported");
+        }
+
+        @Override
+        public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+            try {
+                DbDowngradeHelper.parse(mSchemaFile).onDowngrade(db, oldVersion, newVersion);
+            } catch (Exception e) {
+                throw new DowngradeFailException(e);
+            }
+        }
+    }
+
+    private static class DowngradeFailException extends RuntimeException {
+        public DowngradeFailException(Exception e) {
+            super(e);
+        }
+    }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/model/GridBackupTableTest.java b/robolectric_tests/src/com/android/launcher3/model/GridBackupTableTest.java
new file mode 100644
index 0000000..53287a9
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/model/GridBackupTableTest.java
@@ -0,0 +1,115 @@
+package com.android.launcher3.model;
+
+
+import static android.database.DatabaseUtils.queryNumEntries;
+
+import static com.android.launcher3.LauncherSettings.Favorites.BACKUP_TABLE_NAME;
+import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
+import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.ContentValues;
+import android.graphics.Point;
+
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.LauncherSettings.Settings;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+/**
+ * Unit tests for {@link GridBackupTable}
+ */
+@RunWith(RobolectricTestRunner.class)
+public class GridBackupTableTest extends BaseGridChangesTestCase {
+
+    private static final int BACKUP_ITEM_COUNT = 12;
+
+    @Before
+    public void setupGridData() {
+        createGrid(new int[][][]{{
+                { APP_ICON, APP_ICON, SHORTCUT, SHORTCUT},
+                { SHORTCUT, SHORTCUT, NO__ICON, NO__ICON},
+                { NO__ICON, NO__ICON, SHORTCUT, SHORTCUT},
+                { APP_ICON, SHORTCUT, SHORTCUT, APP_ICON},
+        }});
+        assertEquals(BACKUP_ITEM_COUNT, queryNumEntries(mDb, TABLE_NAME));
+    }
+
+    @Test
+    public void backupTableCreated() {
+        GridBackupTable backupTable = new GridBackupTable(mContext, mDb, 4, 4, 4);
+        assertFalse(backupTable.backupOrRestoreAsNeeded());
+        Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
+
+        assertTrue(tableExists(mDb, BACKUP_TABLE_NAME));
+
+        // One extra entry for properties
+        assertEquals(BACKUP_ITEM_COUNT + 1, queryNumEntries(mDb, BACKUP_TABLE_NAME));
+    }
+
+    @Test
+    public void backupTableRestored() {
+        assertFalse(new GridBackupTable(mContext, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
+        Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
+
+        // Delete entries
+        mDb.delete(TABLE_NAME, null, null);
+        assertEquals(0, queryNumEntries(mDb, TABLE_NAME));
+
+        GridBackupTable backupTable = new GridBackupTable(mContext, mDb, 3, 3, 3);
+        assertTrue(backupTable.backupOrRestoreAsNeeded());
+
+        // Items have been restored
+        assertEquals(BACKUP_ITEM_COUNT, queryNumEntries(mDb, TABLE_NAME));
+
+        Point outSize = new Point();
+        assertEquals(4, backupTable.getRestoreHotseatAndGridSize(outSize));
+        assertEquals(4, outSize.x);
+        assertEquals(4, outSize.y);
+    }
+
+    @Test
+    public void backupTableRemovedOnAdd() {
+        assertFalse(new GridBackupTable(mContext, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
+        Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
+
+        assertTrue(tableExists(mDb, BACKUP_TABLE_NAME));
+
+        addItem(1, 2, DESKTOP, 1, 1);
+        assertFalse(tableExists(mDb, BACKUP_TABLE_NAME));
+    }
+
+    @Test
+    public void backupTableRemovedOnDelete() {
+        assertFalse(new GridBackupTable(mContext, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
+        Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
+
+        assertTrue(tableExists(mDb, BACKUP_TABLE_NAME));
+
+        mContext.getContentResolver().delete(Favorites.CONTENT_URI, null, null);
+        assertFalse(tableExists(mDb, BACKUP_TABLE_NAME));
+    }
+
+    @Test
+    public void backupTableRetainedOnUpdate() {
+        assertFalse(new GridBackupTable(mContext, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
+        Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
+
+        assertTrue(tableExists(mDb, BACKUP_TABLE_NAME));
+
+        ContentValues values = new ContentValues();
+        values.put(Favorites.RANK, 4);
+        // Something was updated
+        assertTrue(mContext.getContentResolver()
+                .update(Favorites.CONTENT_URI, values, null, null) > 0);
+
+        // Backup table remains
+        assertTrue(tableExists(mDb, BACKUP_TABLE_NAME));
+    }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
new file mode 100644
index 0000000..53f6a06
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
@@ -0,0 +1,362 @@
+package com.android.launcher3.model;
+
+import static com.android.launcher3.model.GridSizeMigrationTask.getWorkspaceScreenIds;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.database.Cursor;
+import android.graphics.Point;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.config.FlagOverrideRule;
+import com.android.launcher3.model.GridSizeMigrationTask.MultiStepMigrationTask;
+import com.android.launcher3.util.IntArray;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.HashSet;
+import java.util.LinkedList;
+
+/**
+ * Unit tests for {@link GridSizeMigrationTask}
+ */
+@RunWith(RobolectricTestRunner.class)
+public class GridSizeMigrationTaskTest extends BaseGridChangesTestCase {
+
+    @Rule
+    public final FlagOverrideRule flags = new FlagOverrideRule();
+
+    private HashSet<String> mValidPackages;
+    private InvariantDeviceProfile mIdp;
+
+    @Before
+    public void setUp() {
+        mValidPackages = new HashSet<>();
+        mValidPackages.add(TEST_PACKAGE);
+        mIdp = new InvariantDeviceProfile();
+    }
+
+    @Test
+    public void testHotseatMigration_apps_dropped() throws Exception {
+        int[] hotseatItems = {
+                addItem(APP_ICON, 0, HOTSEAT, 0, 0),
+                addItem(SHORTCUT, 1, HOTSEAT, 0, 0),
+                -1,
+                addItem(SHORTCUT, 3, HOTSEAT, 0, 0),
+                addItem(APP_ICON, 4, HOTSEAT, 0, 0),
+        };
+
+        mIdp.numHotseatIcons = 3;
+        new GridSizeMigrationTask(mContext, mDb, mValidPackages, 5, 3)
+                .migrateHotseat();
+        // First item is dropped as it has the least weight.
+        verifyHotseat(hotseatItems[1], hotseatItems[3], hotseatItems[4]);
+    }
+
+    @Test
+    public void testHotseatMigration_shortcuts_dropped() throws Exception {
+        int[] hotseatItems = {
+                addItem(APP_ICON, 0, HOTSEAT, 0, 0),
+                addItem(30, 1, HOTSEAT, 0, 0),
+                -1,
+                addItem(SHORTCUT, 3, HOTSEAT, 0, 0),
+                addItem(10, 4, HOTSEAT, 0, 0),
+        };
+
+        mIdp.numHotseatIcons = 3;
+        new GridSizeMigrationTask(mContext, mDb, mValidPackages, 5, 3)
+                .migrateHotseat();
+        // First item is dropped as it has the least weight.
+        verifyHotseat(hotseatItems[1], hotseatItems[3], hotseatItems[4]);
+    }
+
+    private void verifyHotseat(int... sortedIds) {
+        int screenId = 0;
+        int total = 0;
+
+        for (int id : sortedIds) {
+            Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
+                    new String[]{LauncherSettings.Favorites._ID},
+                    "container=-101 and screen=" + screenId, null, null, null);
+
+            if (id == -1) {
+                assertEquals(0, c.getCount());
+            } else {
+                assertEquals(1, c.getCount());
+                c.moveToNext();
+                assertEquals(id, c.getLong(0));
+                total ++;
+            }
+            c.close();
+
+            screenId++;
+        }
+
+        // Verify that not other entry exist in the DB.
+        Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
+                new String[]{LauncherSettings.Favorites._ID},
+                "container=-101", null, null, null);
+        assertEquals(total, c.getCount());
+        c.close();
+    }
+
+    @Test
+    public void testWorkspace_empty_row_column_removed() throws Exception {
+        int[][][] ids = createGrid(new int[][][]{{
+                {  0,  0, -1,  1},
+                {  3,  1, -1,  4},
+                { -1, -1, -1, -1},
+                {  5,  2, -1,  6},
+        }});
+
+        new GridSizeMigrationTask(mContext, mDb, mValidPackages,
+                new Point(4, 4), new Point(3, 3)).migrateWorkspace();
+
+        // Column 2 and row 2 got removed.
+        verifyWorkspace(new int[][][] {{
+                {ids[0][0][0], ids[0][0][1], ids[0][0][3]},
+                {ids[0][1][0], ids[0][1][1], ids[0][1][3]},
+                {ids[0][3][0], ids[0][3][1], ids[0][3][3]},
+        }});
+    }
+
+    @Test
+    public void testWorkspace_new_screen_created() throws Exception {
+        int[][][] ids = createGrid(new int[][][]{{
+                {  0,  0,  0,  1},
+                {  3,  1,  0,  4},
+                { -1, -1, -1, -1},
+                {  5,  2, -1,  6},
+        }});
+
+        new GridSizeMigrationTask(mContext, mDb, mValidPackages,
+                new Point(4, 4), new Point(3, 3)).migrateWorkspace();
+
+        // Items in the second column get moved to new screen
+        verifyWorkspace(new int[][][] {{
+                {ids[0][0][0], ids[0][0][1], ids[0][0][3]},
+                {ids[0][1][0], ids[0][1][1], ids[0][1][3]},
+                {ids[0][3][0], ids[0][3][1], ids[0][3][3]},
+        }, {
+                {ids[0][0][2], ids[0][1][2], -1},
+        }});
+    }
+
+    @Test
+    public void testWorkspace_items_merged_in_next_screen() throws Exception {
+        int[][][] ids = createGrid(new int[][][]{{
+                {  0,  0,  0,  1},
+                {  3,  1,  0,  4},
+                { -1, -1, -1, -1},
+                {  5,  2, -1,  6},
+        },{
+                {  0,  0, -1,  1},
+                {  3,  1, -1,  4},
+        }});
+
+        new GridSizeMigrationTask(mContext, mDb, mValidPackages,
+                new Point(4, 4), new Point(3, 3)).migrateWorkspace();
+
+        // Items in the second column of the first screen should get placed on the 3rd
+        // row of the second screen
+        verifyWorkspace(new int[][][] {{
+                {ids[0][0][0], ids[0][0][1], ids[0][0][3]},
+                {ids[0][1][0], ids[0][1][1], ids[0][1][3]},
+                {ids[0][3][0], ids[0][3][1], ids[0][3][3]},
+        }, {
+                {ids[1][0][0], ids[1][0][1], ids[1][0][3]},
+                {ids[1][1][0], ids[1][1][1], ids[1][1][3]},
+                {ids[0][0][2], ids[0][1][2], -1},
+        }});
+    }
+
+    @Test
+    public void testWorkspace_items_not_merged_in_next_screen() throws Exception {
+        // First screen has 2 items that need to be moved, but second screen has only one
+        // empty space after migration (top-left corner)
+        int[][][] ids = createGrid(new int[][][]{{
+                {  0,  0,  0,  1},
+                {  3,  1,  0,  4},
+                { -1, -1, -1, -1},
+                {  5,  2, -1,  6},
+        },{
+                { -1,  0, -1,  1},
+                {  3,  1, -1,  4},
+                { -1, -1, -1, -1},
+                {  5,  2, -1,  6},
+        }});
+
+        new GridSizeMigrationTask(mContext, mDb, mValidPackages,
+                new Point(4, 4), new Point(3, 3)).migrateWorkspace();
+
+        // Items in the second column of the first screen should get placed on a new screen.
+        verifyWorkspace(new int[][][] {{
+                {ids[0][0][0], ids[0][0][1], ids[0][0][3]},
+                {ids[0][1][0], ids[0][1][1], ids[0][1][3]},
+                {ids[0][3][0], ids[0][3][1], ids[0][3][3]},
+        }, {
+                {          -1, ids[1][0][1], ids[1][0][3]},
+                {ids[1][1][0], ids[1][1][1], ids[1][1][3]},
+                {ids[1][3][0], ids[1][3][1], ids[1][3][3]},
+        }, {
+                {ids[0][0][2], ids[0][1][2], -1},
+        }});
+    }
+
+    @Test
+    public void testWorkspace_first_row_blocked() throws Exception {
+        if (!FeatureFlags.QSB_ON_FIRST_SCREEN) {
+            return;
+        }
+        // The first screen has one item on the 4th column which needs moving, as the first row
+        // will be kept empty.
+        int[][][] ids = createGrid(new int[][][]{{
+                { -1, -1, -1, -1},
+                {  3,  1,  7,  0},
+                {  8,  7,  7, -1},
+                {  5,  2,  7, -1},
+        }}, 0);
+
+        new GridSizeMigrationTask(mContext, mDb, mValidPackages,
+                new Point(4, 4), new Point(3, 4)).migrateWorkspace();
+
+        // Items in the second column of the first screen should get placed on a new screen.
+        verifyWorkspace(new int[][][] {{
+                {          -1,           -1,           -1},
+                {ids[0][1][0], ids[0][1][1], ids[0][1][2]},
+                {ids[0][2][0], ids[0][2][1], ids[0][2][2]},
+                {ids[0][3][0], ids[0][3][1], ids[0][3][2]},
+        }, {
+                {ids[0][1][3]},
+        }});
+    }
+
+    @Test
+    public void testWorkspace_items_moved_to_empty_first_row() throws Exception {
+        if (!FeatureFlags.QSB_ON_FIRST_SCREEN) {
+            return;
+        }
+        // Items will get moved to the next screen to keep the first screen empty.
+        int[][][] ids = createGrid(new int[][][]{{
+                { -1, -1, -1, -1},
+                {  0,  1,  0,  0},
+                {  8,  7,  7, -1},
+                {  5,  6,  7, -1},
+        }}, 0);
+
+        new GridSizeMigrationTask(mContext, mDb, mValidPackages,
+                new Point(4, 4), new Point(3, 3)).migrateWorkspace();
+
+        // Items in the second column of the first screen should get placed on a new screen.
+        verifyWorkspace(new int[][][] {{
+                {          -1,           -1,           -1},
+                {ids[0][2][0], ids[0][2][1], ids[0][2][2]},
+                {ids[0][3][0], ids[0][3][1], ids[0][3][2]},
+        }, {
+                {ids[0][1][1], ids[0][1][0], ids[0][1][2]},
+                {ids[0][1][3]},
+        }});
+    }
+
+    /**
+     * Verifies that the workspace items are arranged in the provided order.
+     * @param ids A 3d array where the first dimension represents the screen, and the rest two
+     *            represent the workspace grid.
+     */
+    private void verifyWorkspace(int[][][] ids) {
+        IntArray allScreens = getWorkspaceScreenIds(mDb);
+        assertEquals(ids.length, allScreens.size());
+        int total = 0;
+
+        for (int i = 0; i < ids.length; i++) {
+            int screenId = allScreens.get(i);
+            for (int y = 0; y < ids[i].length; y++) {
+                for (int x = 0; x < ids[i][y].length; x++) {
+                    int id = ids[i][y][x];
+
+                    Cursor c = mContext.getContentResolver().query(
+                            LauncherSettings.Favorites.CONTENT_URI,
+                            new String[]{LauncherSettings.Favorites._ID},
+                            "container=-100 and screen=" + screenId +
+                                    " and cellX=" + x + " and cellY=" + y, null, null, null);
+                    if (id == -1) {
+                        assertEquals(0, c.getCount());
+                    } else {
+                        assertEquals(1, c.getCount());
+                        c.moveToNext();
+                        assertEquals(String.format("Failed to verify item at %d %d, %d", i, y, x),
+                                id, c.getLong(0));
+                        total++;
+                    }
+                    c.close();
+                }
+            }
+        }
+
+        // Verify that not other entry exist in the DB.
+        Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
+                new String[]{LauncherSettings.Favorites._ID},
+                "container=-100", null, null, null);
+        assertEquals(total, c.getCount());
+        c.close();
+    }
+
+    @Test
+    public void testMultiStepMigration_small_to_large() throws Exception {
+        MultiStepMigrationTaskVerifier verifier = new MultiStepMigrationTaskVerifier();
+        verifier.migrate(new Point(3, 3), new Point(5, 5));
+        verifier.assertCompleted();
+    }
+
+    @Test
+    public void testMultiStepMigration_large_to_small() throws Exception {
+        MultiStepMigrationTaskVerifier verifier = new MultiStepMigrationTaskVerifier(
+                5, 5, 4, 4,
+                4, 4, 3, 4
+        );
+        verifier.migrate(new Point(5, 5), new Point(3, 4));
+        verifier.assertCompleted();
+    }
+
+    @Test
+    public void testMultiStepMigration_zig_zag() throws Exception {
+        MultiStepMigrationTaskVerifier verifier = new MultiStepMigrationTaskVerifier(
+                5, 7, 4, 7,
+                4, 7, 3, 7
+        );
+        verifier.migrate(new Point(5, 5), new Point(3, 7));
+        verifier.assertCompleted();
+    }
+
+    private static class MultiStepMigrationTaskVerifier extends MultiStepMigrationTask {
+
+        private final LinkedList<Point> mPoints;
+
+        public MultiStepMigrationTaskVerifier(int... points) {
+            super(null, null, null);
+
+            mPoints = new LinkedList<>();
+            for (int i = 0; i < points.length; i += 2) {
+                mPoints.add(new Point(points[i], points[i + 1]));
+            }
+        }
+
+        @Override
+        protected boolean runStepTask(Point sourceSize, Point nextSize) throws Exception {
+            assertEquals(sourceSize, mPoints.poll());
+            assertEquals(nextSize, mPoints.poll());
+            return false;
+        }
+
+        public void assertCompleted() {
+            assertTrue(mPoints.isEmpty());
+        }
+    }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
new file mode 100644
index 0000000..e9324f9
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
@@ -0,0 +1,70 @@
+package com.android.launcher3.model;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.compat.PackageInstallerCompat;
+import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.Arrays;
+import java.util.HashSet;
+
+/**
+ * Tests for {@link PackageInstallStateChangedTask}
+ */
+@RunWith(RobolectricTestRunner.class)
+public class PackageInstallStateChangedTaskTest extends BaseModelUpdateTaskTestCase {
+
+    @Before
+    public void initData() throws Exception {
+        initializeData("/package_install_state_change_task_data.txt");
+    }
+
+    private PackageInstallStateChangedTask newTask(String pkg, int progress) {
+        int state = PackageInstallerCompat.STATUS_INSTALLING;
+        PackageInstallInfo installInfo = new PackageInstallInfo(pkg, state, progress);
+        return new PackageInstallStateChangedTask(installInfo);
+    }
+
+    @Test
+    public void testSessionUpdate_ignore_installed() throws Exception {
+        executeTaskForTest(newTask("app1", 30));
+
+        // No shortcuts were updated
+        verifyProgressUpdate(0);
+    }
+
+    @Test
+    public void testSessionUpdate_shortcuts_updated() throws Exception {
+        executeTaskForTest(newTask("app3", 30));
+
+        verifyProgressUpdate(30, 5, 6, 7);
+    }
+
+    @Test
+    public void testSessionUpdate_widgets_updated() throws Exception {
+        executeTaskForTest(newTask("app4", 30));
+
+        verifyProgressUpdate(30, 8, 9);
+    }
+
+    private void verifyProgressUpdate(int progress, Integer... idsUpdated) {
+        HashSet<Integer> updates = new HashSet<>(Arrays.asList(idsUpdated));
+        for (ItemInfo info : bgDataModel.itemsIdMap) {
+            if (info instanceof WorkspaceItemInfo) {
+                assertEquals(updates.contains(info.id) ? progress: 0,
+                        ((WorkspaceItemInfo) info).getInstallProgress());
+            } else {
+                assertEquals(updates.contains(info.id) ? progress: -1,
+                        ((LauncherAppWidgetInfo) info).installProgress);
+            }
+        }
+    }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/popup/PopupPopulatorTest.java b/robolectric_tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
new file mode 100644
index 0000000..83bf7da
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
@@ -0,0 +1,147 @@
+/*
+ * 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.popup;
+
+import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS;
+import static com.android.launcher3.popup.PopupPopulator.NUM_DYNAMIC;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.content.pm.ShortcutInfo;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Tests the sorting and filtering of shortcuts in {@link PopupPopulator}.
+ */
+@RunWith(RobolectricTestRunner.class)
+public class PopupPopulatorTest {
+
+    @Test
+    public void testSortAndFilterShortcuts() {
+        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 0), 3, 0);
+        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(0, 3), 0, 3);
+        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(5, 0), MAX_SHORTCUTS, 0);
+        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(0, 5), 0, MAX_SHORTCUTS);
+        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 3),
+                MAX_SHORTCUTS - NUM_DYNAMIC, NUM_DYNAMIC);
+        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(5, 5),
+                MAX_SHORTCUTS - NUM_DYNAMIC, NUM_DYNAMIC);
+        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(5, 1), MAX_SHORTCUTS - 1, 1);
+        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(1, 5), 1, MAX_SHORTCUTS - 1);
+        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(5, 3),
+                MAX_SHORTCUTS - NUM_DYNAMIC, NUM_DYNAMIC);
+        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 5),
+                MAX_SHORTCUTS - NUM_DYNAMIC, NUM_DYNAMIC);
+    }
+
+    @Test
+    public void testDeDupeShortcutId() {
+        // Successfully remove one of the shortcuts
+        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 0), 2, 0, generateId(true, 1));
+        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(0, 3), 0, 2, generateId(false, 1));
+        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(2, 2), 2, 1, generateId(false, 1));
+        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(2, 2), 1, 2, generateId(true, 1));
+        // Successfully keep all shortcuts when id doesn't exist
+        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 0), 3, 0, generateId(false, 1));
+        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 0), 3, 0, generateId(true, 4));
+        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(2, 2), 2, 2, generateId(false, 4));
+        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(2, 2), 2, 2, generateId(true, 4));
+    }
+
+    private String generateId(boolean isStatic, int rank) {
+        return (isStatic ? "static" : "dynamic") + rank;
+    }
+
+    private void filterShortcutsAndAssertNumStaticAndDynamic(
+            List<ShortcutInfo> shortcuts, int expectedStatic, int expectedDynamic) {
+        filterShortcutsAndAssertNumStaticAndDynamic(shortcuts, expectedStatic, expectedDynamic, null);
+    }
+
+    private void filterShortcutsAndAssertNumStaticAndDynamic(List<ShortcutInfo> shortcuts,
+            int expectedStatic, int expectedDynamic, String shortcutIdToRemove) {
+        Collections.shuffle(shortcuts);
+        List<ShortcutInfo> filteredShortcuts = PopupPopulator.sortAndFilterShortcuts(
+                shortcuts, shortcutIdToRemove);
+        assertIsSorted(filteredShortcuts);
+
+        int numStatic = 0;
+        int numDynamic = 0;
+        for (ShortcutInfo shortcut : filteredShortcuts) {
+            if (shortcut.isDeclaredInManifest()) {
+                numStatic++;
+            }
+            if (shortcut.isDynamic()) {
+                numDynamic++;
+            }
+        }
+        assertEquals(expectedStatic, numStatic);
+        assertEquals(expectedDynamic, numDynamic);
+    }
+
+    private void assertIsSorted(List<ShortcutInfo> shortcuts) {
+        int lastStaticRank = -1;
+        int lastDynamicRank = -1;
+        boolean hasSeenDynamic = false;
+        for (ShortcutInfo shortcut : shortcuts) {
+            int rank = shortcut.getRank();
+            if (shortcut.isDeclaredInManifest()) {
+                assertFalse("Static shortcuts should come before all dynamic shortcuts.",
+                        hasSeenDynamic);
+                assertTrue(rank > lastStaticRank);
+                lastStaticRank = rank;
+            }
+            if (shortcut.isDynamic()) {
+                hasSeenDynamic = true;
+                assertTrue(rank > lastDynamicRank);
+                lastDynamicRank = rank;
+            }
+        }
+    }
+
+    private List<ShortcutInfo> createShortcutsList(int numStatic, int numDynamic) {
+        List<ShortcutInfo> shortcuts = new ArrayList<>();
+        for (int i = 0; i < numStatic; i++) {
+            shortcuts.add(createInfo(true, i));
+        }
+        for (int i = 0; i < numDynamic; i++) {
+            shortcuts.add(createInfo(false, i));
+        }
+        return shortcuts;
+    }
+
+    private ShortcutInfo createInfo(boolean isStatic, int rank) {
+        ShortcutInfo info = spy(new ShortcutInfo.Builder(
+                RuntimeEnvironment.application, generateId(isStatic, rank))
+                .setRank(rank)
+                .build());
+        doReturn(isStatic).when(info).isDeclaredInManifest();
+        doReturn(!isStatic).when(info).isDynamic();
+        return info;
+    }
+}
\ No newline at end of file
diff --git a/robolectric_tests/src/com/android/launcher3/util/GridOccupancyTest.java b/robolectric_tests/src/com/android/launcher3/util/GridOccupancyTest.java
new file mode 100644
index 0000000..aa51ad2
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/util/GridOccupancyTest.java
@@ -0,0 +1,67 @@
+package com.android.launcher3.util;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Unit tests for {@link GridOccupancy}
+ */
+@RunWith(RobolectricTestRunner.class)
+public class GridOccupancyTest {
+
+    @Test
+    public void testFindVacantCell() {
+        GridOccupancy grid = initGrid(4,
+                1, 1, 1, 0, 0,
+                0, 0, 1, 1, 0,
+                0, 0, 0, 0, 0,
+                1, 1, 0, 0, 0
+        );
+
+        int[] vacant = new int[2];
+        assertTrue(grid.findVacantCell(vacant, 2, 2));
+        assertEquals(vacant[0], 0);
+        assertEquals(vacant[1], 1);
+
+        assertTrue(grid.findVacantCell(vacant, 3, 2));
+        assertEquals(vacant[0], 2);
+        assertEquals(vacant[1], 2);
+
+        assertFalse(grid.findVacantCell(vacant, 3, 3));
+    }
+
+    @Test
+    public void testIsRegionVacant() {
+        GridOccupancy grid = initGrid(4,
+                1, 1, 1, 0, 0,
+                0, 0, 1, 1, 0,
+                0, 0, 0, 0, 0,
+                1, 1, 0, 0, 0
+        );
+
+        assertTrue(grid.isRegionVacant(4, 0, 1, 4));
+        assertTrue(grid.isRegionVacant(0, 1, 2, 2));
+        assertTrue(grid.isRegionVacant(2, 2, 3, 2));
+
+        assertFalse(grid.isRegionVacant(3, 0, 2, 4));
+        assertFalse(grid.isRegionVacant(0, 0, 2, 1));
+    }
+
+    private GridOccupancy initGrid(int rows, int... cells) {
+        int cols = cells.length / rows;
+        int i = 0;
+        GridOccupancy grid = new GridOccupancy(cols, rows);
+        for (int y = 0; y < rows; y++) {
+            for (int x = 0; x < cols; x++) {
+                grid.cells[x][y] = cells[i] != 0;
+                i++;
+            }
+        }
+        return grid;
+    }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/util/IntSetTest.java b/robolectric_tests/src/com/android/launcher3/util/IntSetTest.java
new file mode 100644
index 0000000..f846de5
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/util/IntSetTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Robolectric unit tests for {@link IntSet}
+ */
+@RunWith(RobolectricTestRunner.class)
+public class IntSetTest {
+
+    @Test
+    public void shouldBeEmptyInitially() {
+        IntSet set = new IntSet();
+        assertThat(set.size()).isEqualTo(0);
+    }
+
+    @Test
+    public void oneElementSet() {
+        IntSet set = new IntSet();
+        set.add(2);
+        assertThat(set.size()).isEqualTo(1);
+        assertTrue(set.contains(2));
+        assertFalse(set.contains(1));
+    }
+
+
+    @Test
+    public void twoElementSet() {
+        IntSet set = new IntSet();
+        set.add(2);
+        set.add(1);
+        assertThat(set.size()).isEqualTo(2);
+        assertTrue(set.contains(2));
+        assertTrue(set.contains(1));
+    }
+
+    @Test
+    public void threeElementSet() {
+        IntSet set = new IntSet();
+        set.add(2);
+        set.add(1);
+        set.add(10);
+        assertThat(set.size()).isEqualTo(3);
+        assertEquals("1, 2, 10", set.mArray.toConcatString());
+    }
+
+
+    @Test
+    public void duplicateEntries() {
+        IntSet set = new IntSet();
+        set.add(2);
+        set.add(2);
+        assertEquals(1, set.size());
+    }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/util/TestLauncherProvider.java b/robolectric_tests/src/com/android/launcher3/util/TestLauncherProvider.java
new file mode 100644
index 0000000..31e303e
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/util/TestLauncherProvider.java
@@ -0,0 +1,51 @@
+package com.android.launcher3.util;
+
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+
+import com.android.launcher3.LauncherProvider;
+
+/**
+ * An extension of LauncherProvider backed up by in-memory database.
+ */
+public class TestLauncherProvider extends LauncherProvider {
+
+    @Override
+    public boolean onCreate() {
+        return true;
+    }
+
+    @Override
+    protected synchronized void createDbIfNotExists() {
+        if (mOpenHelper == null) {
+            mOpenHelper = new MyDatabaseHelper(getContext());
+        }
+    }
+
+    public SQLiteDatabase getDb() {
+        createDbIfNotExists();
+        return mOpenHelper.getWritableDatabase();
+    }
+
+    @Override
+    protected void notifyListeners() { }
+
+    private static class MyDatabaseHelper extends DatabaseHelper {
+        public MyDatabaseHelper(Context context) {
+            super(context, null, null);
+            initIds();
+        }
+
+        @Override
+        public long getDefaultUserSerial() {
+            return 0;
+        }
+
+        @Override
+        protected void onEmptyDbCreated() { }
+
+        @Override
+        protected void handleOneTimeDataUpgrade(SQLiteDatabase db) { }
+    }
+}
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..b52bd4f
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,2 @@
+include ':IconLoader'
+project(':IconLoader').projectDir = new File(rootDir, 'iconloaderlib')
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index b614ec9..65f9d6b 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -23,17 +23,22 @@
 import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
 import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
 
+import android.animation.Animator;
 import android.annotation.SuppressLint;
 import android.content.Context;
-import android.support.annotation.IntDef;
 import android.util.AttributeSet;
 import android.util.Pair;
 import android.view.MotionEvent;
 import android.view.View;
 import android.widget.LinearLayout;
 
+import androidx.annotation.IntDef;
+import androidx.annotation.Nullable;
+
 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;
 
 import java.lang.annotation.Retention;
@@ -52,8 +57,9 @@
             TYPE_WIDGETS_FULL_SHEET,
             TYPE_ON_BOARD_POPUP,
             TYPE_DISCOVERY_BOUNCE,
+            TYPE_SNACKBAR,
+            TYPE_LISTENER,
 
-            TYPE_QUICKSTEP_PREVIEW,
             TYPE_TASK_MENU,
             TYPE_OPTIONS_POPUP
     })
@@ -66,25 +72,32 @@
     public static final int TYPE_WIDGETS_FULL_SHEET = 1 << 4;
     public static final int TYPE_ON_BOARD_POPUP = 1 << 5;
     public static final int TYPE_DISCOVERY_BOUNCE = 1 << 6;
+    public static final int TYPE_SNACKBAR = 1 << 7;
+    public static final int TYPE_LISTENER = 1 << 8;
 
     // Popups related to quickstep UI
-    public static final int TYPE_QUICKSTEP_PREVIEW = 1 << 7;
-    public static final int TYPE_TASK_MENU = 1 << 8;
-    public static final int TYPE_OPTIONS_POPUP = 1 << 9;
+    public static final int TYPE_TASK_MENU = 1 << 9;
+    public static final int TYPE_OPTIONS_POPUP = 1 << 10;
 
     public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP
             | TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET
-            | TYPE_QUICKSTEP_PREVIEW | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE | TYPE_TASK_MENU
-            | TYPE_OPTIONS_POPUP;
+            | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE | TYPE_TASK_MENU
+            | TYPE_OPTIONS_POPUP | TYPE_SNACKBAR | TYPE_LISTENER;
 
     // Type of popups which should be kept open during launcher rebind
     public static final int TYPE_REBIND_SAFE = TYPE_WIDGETS_FULL_SHEET
-            | TYPE_QUICKSTEP_PREVIEW | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE;
+            | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE;
 
     // 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;
+    public static final int TYPE_HIDE_BACK_BUTTON = TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE
+            | TYPE_SNACKBAR | TYPE_WIDGET_RESIZE_FRAME | TYPE_LISTENER;
 
-    public static final int TYPE_ACCESSIBLE = TYPE_ALL & ~TYPE_DISCOVERY_BOUNCE;
+    public static final int TYPE_ACCESSIBLE = TYPE_ALL & ~TYPE_DISCOVERY_BOUNCE & ~TYPE_LISTENER;
+
+    // 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 ;
 
     protected boolean mIsOpen;
 
@@ -106,7 +119,7 @@
     }
 
     public final void close(boolean animate) {
-        animate &= !Utilities.isPowerSaverPreventingAnimation(getContext());
+        animate &= Utilities.areAnimationsEnabled(getContext());
         if (mIsOpen) {
             BaseActivity.fromContext(getContext()).getUserEventDispatcher()
                     .resetElapsedContainerMillis("container closed");
@@ -117,15 +130,24 @@
 
     protected abstract void handleClose(boolean animate);
 
+    /**
+     * Creates a user-controlled animation to hint that the view will be closed if completed.
+     * @param distanceToMove The max distance that elements should move from their starting point.
+     */
+    public @Nullable Animator createHintCloseAnim(float distanceToMove) {
+        return null;
+    }
+
     public abstract void logActionCommand(int command);
 
+    public int getLogContainerType() {
+        return ContainerType.DEFAULT_CONTAINERTYPE;
+    }
+
     public final boolean isOpen() {
         return mIsOpen;
     }
 
-    protected void onWidgetsBound() {
-    }
-
     protected abstract boolean isOfType(@FloatingViewType int type);
 
     /** @return Whether the back is consumed. If false, Launcher will handle the back as well. */
@@ -151,7 +173,7 @@
         if (mIsOpen) {
             sendAccessibilityEvent(TYPE_VIEW_FOCUSED);
         }
-        BaseDraggingActivity.fromContext(getContext()).getDragLayer()
+        ActivityContext.lookupContext(getContext()).getDragLayer()
                 .sendAccessibilityEvent(TYPE_WINDOW_CONTENT_CHANGED);
     }
 
@@ -160,8 +182,9 @@
     }
 
     protected static <T extends AbstractFloatingView> T getOpenView(
-            BaseDraggingActivity activity, @FloatingViewType int type) {
+            ActivityContext activity, @FloatingViewType int type) {
         BaseDragLayer dragLayer = activity.getDragLayer();
+        if (dragLayer == null) return null;
         // Iterate in reverse order. AbstractFloatingView is added later to the dragLayer,
         // and will be one of the last views.
         for (int i = dragLayer.getChildCount() - 1; i >= 0; i--) {
@@ -176,7 +199,7 @@
         return null;
     }
 
-    public static void closeOpenContainer(BaseDraggingActivity activity,
+    public static void closeOpenContainer(ActivityContext activity,
             @FloatingViewType int type) {
         AbstractFloatingView view = getOpenView(activity, type);
         if (view != null) {
@@ -184,7 +207,7 @@
         }
     }
 
-    public static void closeOpenViews(BaseDraggingActivity activity, boolean animate,
+    public static void closeOpenViews(ActivityContext activity, boolean animate,
             @FloatingViewType int type) {
         BaseDragLayer dragLayer = activity.getDragLayer();
         // Iterate in reverse order. AbstractFloatingView is added later to the dragLayer,
@@ -200,20 +223,31 @@
         }
     }
 
-    public static void closeAllOpenViews(BaseDraggingActivity activity, boolean animate) {
+    public static void closeAllOpenViews(ActivityContext activity, boolean animate) {
         closeOpenViews(activity, animate, TYPE_ALL);
         activity.finishAutoCancelActionMode();
     }
 
-    public static void closeAllOpenViews(BaseDraggingActivity activity) {
+    public static void closeAllOpenViews(ActivityContext activity) {
         closeAllOpenViews(activity, true);
     }
 
-    public static AbstractFloatingView getTopOpenView(BaseDraggingActivity activity) {
+    public static void closeAllOpenViewsExcept(ActivityContext activity, boolean animate,
+                                               @FloatingViewType int type) {
+        closeOpenViews(activity, animate, TYPE_ALL & ~type);
+        activity.finishAutoCancelActionMode();
+    }
+
+    public static void closeAllOpenViewsExcept(ActivityContext activity,
+                                               @FloatingViewType int type) {
+        closeAllOpenViewsExcept(activity, true, type);
+    }
+
+    public static AbstractFloatingView getTopOpenView(ActivityContext activity) {
         return getTopOpenViewWithType(activity, TYPE_ALL);
     }
 
-    public static AbstractFloatingView getTopOpenViewWithType(BaseDraggingActivity activity,
+    public static AbstractFloatingView getTopOpenViewWithType(ActivityContext activity,
             @FloatingViewType int type) {
         return getOpenView(activity, type);
     }
diff --git a/src/com/android/launcher3/AllAppsList.java b/src/com/android/launcher3/AllAppsList.java
index 5eb6cc7..733f295 100644
--- a/src/com/android/launcher3/AllAppsList.java
+++ b/src/com/android/launcher3/AllAppsList.java
@@ -22,12 +22,11 @@
 import android.content.pm.LauncherActivityInfo;
 import android.os.Process;
 import android.os.UserHandle;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
 import android.util.Log;
 
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.PackageInstallerCompat;
+import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.util.FlagOp;
 import com.android.launcher3.util.ItemInfoMatcher;
 
@@ -35,6 +34,9 @@
 import java.util.HashSet;
 import java.util.List;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 
 /**
  * Stores the list of all applications for the all apps view.
@@ -91,7 +93,7 @@
         // only if not yet installed
         if (applicationInfo == null) {
             PromiseAppInfo info = new PromiseAppInfo(installInfo);
-            mIconCache.getTitleAndIcon(info, info.usingLowResIcon);
+            mIconCache.getTitleAndIcon(info, info.usingLowResIcon());
             data.add(info);
             added.add(info);
         }
@@ -185,7 +187,7 @@
                 if (user.equals(applicationInfo.user)
                         && packageName.equals(applicationInfo.componentName.getPackageName())) {
                     if (!findActivity(matches, applicationInfo.componentName)) {
-                        Log.w(TAG, "Shortcut will be removed due to app component name change.");
+                        Log.w(TAG, "Changing shortcut target due to app component name change.");
                         removed.add(applicationInfo);
                         data.remove(i);
                     }
diff --git a/src/com/android/launcher3/AppFilter.java b/src/com/android/launcher3/AppFilter.java
index 923835a..9b6166f 100644
--- a/src/com/android/launcher3/AppFilter.java
+++ b/src/com/android/launcher3/AppFilter.java
@@ -3,10 +3,12 @@
 import android.content.ComponentName;
 import android.content.Context;
 
-public class AppFilter {
+import com.android.launcher3.util.ResourceBasedOverride;
+
+public class AppFilter implements ResourceBasedOverride {
 
     public static AppFilter newInstance(Context context) {
-        return Utilities.getOverrideObject(AppFilter.class, context, R.string.app_filter_class);
+        return Overrides.getObject(AppFilter.class, context, R.string.app_filter_class);
     }
 
     public boolean shouldShowApp(ComponentName app) {
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java
index 4d1bedc..d884049 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -26,7 +26,6 @@
 import android.os.UserHandle;
 
 import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.PackageManagerHelper;
 
@@ -82,8 +81,8 @@
         return super.dumpProperties() + " componentName=" + componentName;
     }
 
-    public ShortcutInfo makeShortcut() {
-        return new ShortcutInfo(this);
+    public WorkspaceItemInfo makeWorkspaceItem() {
+        return new WorkspaceItemInfo(this);
     }
 
     public ComponentKey toComponentKey() {
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index 7648e30..f9a8d1b 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -1,10 +1,13 @@
 package com.android.launcher3;
 
+import static com.android.launcher3.LauncherAnimUtils.LAYOUT_HEIGHT;
+import static com.android.launcher3.LauncherAnimUtils.LAYOUT_WIDTH;
+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.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
@@ -21,6 +24,9 @@
 import com.android.launcher3.util.FocusLogic;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
 
+import java.util.ArrayList;
+import java.util.List;
+
 public class AppWidgetResizeFrame extends AbstractFloatingView implements View.OnKeyListener {
     private static final int SNAP_DURATION = 150;
     private static final float DIMMED_HANDLE_ALPHA = 0f;
@@ -39,8 +45,10 @@
 
     private final Launcher mLauncher;
     private final DragViewStateAnnouncer mStateAnnouncer;
+    private final FirstFrameAnimatorHelper mFirstFrameAnimatorHelper;
 
     private final View[] mDragHandles = new View[HANDLE_COUNT];
+    private final List<Rect> mSystemGestureExclusionRects = new ArrayList<>(HANDLE_COUNT);
 
     private LauncherAppWidgetHostView mWidgetView;
     private CellLayout mCellLayout;
@@ -101,6 +109,11 @@
         mBackgroundPadding = getResources()
                 .getDimensionPixelSize(R.dimen.resize_frame_background_padding);
         mTouchTargetWidth = 2 * mBackgroundPadding;
+        mFirstFrameAnimatorHelper = new FirstFrameAnimatorHelper(this);
+
+        for (int i = 0; i < HANDLE_COUNT; i++) {
+            mSystemGestureExclusionRects.add(new Rect());
+        }
     }
 
     @Override
@@ -113,6 +126,19 @@
         }
     }
 
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        super.onLayout(changed, l, t, r, b);
+        if (Utilities.ATLEAST_Q) {
+            for (int i = 0; i < HANDLE_COUNT; i++) {
+                View dragHandle = mDragHandles[i];
+                mSystemGestureExclusionRects.get(i).set(dragHandle.getLeft(), dragHandle.getTop(),
+                        dragHandle.getRight(), dragHandle.getBottom());
+            }
+            setSystemGestureExclusionRects(mSystemGestureExclusionRects);
+        }
+    }
+
     public static void showForWidget(LauncherAppWidgetHostView widget, CellLayout cellLayout) {
         Launcher launcher = Launcher.getLauncher(cellLayout.getContext());
         AbstractFloatingView.closeAllOpenViews(launcher);
@@ -368,12 +394,7 @@
         mDeltaX = 0;
         mDeltaY = 0;
 
-        post(new Runnable() {
-            @Override
-            public void run() {
-                snapToWidget(true);
-            }
-        });
+        post(() -> snapToWidget(true));
     }
 
     /**
@@ -433,24 +454,19 @@
             }
             requestLayout();
         } else {
-            PropertyValuesHolder width = PropertyValuesHolder.ofInt("width", lp.width, newWidth);
-            PropertyValuesHolder height = PropertyValuesHolder.ofInt("height", lp.height,
-                    newHeight);
-            PropertyValuesHolder x = PropertyValuesHolder.ofInt("x", lp.x, newX);
-            PropertyValuesHolder y = PropertyValuesHolder.ofInt("y", lp.y, newY);
-            ObjectAnimator oa =
-                    LauncherAnimUtils.ofPropertyValuesHolder(lp, this, width, height, x, y);
-            oa.addUpdateListener(new AnimatorUpdateListener() {
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    requestLayout();
-                }
-            });
-            AnimatorSet set = LauncherAnimUtils.createAnimatorSet();
+            ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(lp,
+                    PropertyValuesHolder.ofInt(LAYOUT_WIDTH, lp.width, newWidth),
+                    PropertyValuesHolder.ofInt(LAYOUT_HEIGHT, lp.height, newHeight),
+                    PropertyValuesHolder.ofInt(LAYOUT_X, lp.x, newX),
+                    PropertyValuesHolder.ofInt(LAYOUT_Y, lp.y, newY));
+            mFirstFrameAnimatorHelper.addTo(oa).addUpdateListener(a -> requestLayout());
+
+            AnimatorSet set = new AnimatorSet();
             set.play(oa);
             for (int i = 0; i < HANDLE_COUNT; i++) {
-                set.play(LauncherAnimUtils.ofFloat(mDragHandles[i], ALPHA, 1.0f));
+                set.play(mFirstFrameAnimatorHelper.addTo(
+                        ObjectAnimator.ofFloat(mDragHandles[i], ALPHA, 1f)));
             }
-
             set.setDuration(SNAP_DURATION);
             set.start();
         }
diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
index b249c95..d949141 100644
--- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
+++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
@@ -8,8 +8,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.database.Cursor;
-import android.os.Handler;
-import android.support.annotation.WorkerThread;
 import android.util.Log;
 
 import com.android.launcher3.LauncherSettings.Favorites;
@@ -18,6 +16,8 @@
 import com.android.launcher3.provider.RestoreDbTask;
 import com.android.launcher3.util.ContentWriter;
 
+import androidx.annotation.WorkerThread;
+
 public class AppWidgetsRestoredReceiver extends BroadcastReceiver {
 
     private static final String TAG = "AWRestoredReceiver";
@@ -33,16 +33,8 @@
 
             final int[] oldIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_OLD_IDS);
             final int[] newIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
-            if (oldIds.length == newIds.length) {
-                final PendingResult asyncResult = goAsync();
-                new Handler(LauncherModel.getWorkerLooper())
-                        .postAtFrontOfQueue(new Runnable() {
-                            @Override
-                            public void run() {
-                                restoreAppWidgetIds(context, oldIds, newIds);
-                                asyncResult.finish();
-                            }
-                        });
+            if (oldIds != null && newIds != null && oldIds.length == newIds.length) {
+                RestoreDbTask.setRestoredAppWidgetIds(context, oldIds, newIds);
             } else {
                 Log.e(TAG, "Invalid host restored received");
             }
@@ -53,7 +45,7 @@
      * Updates the app widgets whose id has changed during the restore process.
      */
     @WorkerThread
-    static void restoreAppWidgetIds(Context context, int[] oldWidgetIds, int[] newWidgetIds) {
+    public static void restoreAppWidgetIds(Context context, int[] oldWidgetIds, int[] newWidgetIds) {
         AppWidgetHost appWidgetHost = new LauncherAppWidgetHost(context);
         if (FeatureFlags.GO_DISABLE_WIDGETS) {
             Log.e(TAG, "Skipping widget ID remap as widgets not supported");
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index 469b8bb..9724869 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -24,30 +24,34 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
-import android.content.res.XmlResourceParser;
 import android.database.sqlite.SQLiteDatabase;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
-import android.os.Build;
 import android.os.Build.VERSION;
 import android.os.Bundle;
 import android.os.Process;
 import android.text.TextUtils;
 import android.util.ArrayMap;
+import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Pair;
 import android.util.Patterns;
+import android.util.Xml;
+
 import com.android.launcher3.LauncherProvider.SqlArguments;
 import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.graphics.LauncherIcons;
+import com.android.launcher3.icons.GraphicsUtils;
+import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.Thunk;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Locale;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
+import java.io.IOException;
+import java.util.Locale;
+import java.util.function.Supplier;
+
 /**
  * Layout parsing code for auto installs layout
  */
@@ -73,12 +77,8 @@
         if (customizationApkInfo == null) {
             return null;
         }
-        return get(context, customizationApkInfo.first, customizationApkInfo.second,
-                appWidgetHost, callback);
-    }
-
-    static AutoInstallsLayout get(Context context, String pkg, Resources targetRes,
-            AppWidgetHost appWidgetHost, LayoutParserCallback callback) {
+        String pkg = customizationApkInfo.first;
+        Resources targetRes = customizationApkInfo.second;
         InvariantDeviceProfile grid = LauncherAppState.getIDP(context);
 
         // Try with grid size and hotseat count
@@ -111,7 +111,7 @@
 
     // Object Tags
     private static final String TAG_INCLUDE = "include";
-    private static final String TAG_WORKSPACE = "workspace";
+    public static final String TAG_WORKSPACE = "workspace";
     private static final String TAG_APP_ICON = "appicon";
     private static final String TAG_AUTO_INSTALL = "autoinstall";
     private static final String TAG_FOLDER = "folder";
@@ -147,22 +147,19 @@
     private static final String HOTSEAT_CONTAINER_NAME =
             Favorites.containerToString(Favorites.CONTAINER_HOTSEAT);
 
-    private static final String ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE =
-            "com.android.launcher.action.APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE";
-
     @Thunk final Context mContext;
     @Thunk final AppWidgetHost mAppWidgetHost;
     protected final LayoutParserCallback mCallback;
 
     protected final PackageManager mPackageManager;
     protected final Resources mSourceRes;
-    protected final int mLayoutId;
+    protected final Supplier<XmlPullParser> mInitialLayoutSupplier;
 
     private final InvariantDeviceProfile mIdp;
     private final int mRowCount;
     private final int mColumnCount;
 
-    private final long[] mTemp = new long[2];
+    private final int[] mTemp = new int[2];
     @Thunk final ContentValues mValues;
     protected final String mRootTag;
 
@@ -171,6 +168,12 @@
     public AutoInstallsLayout(Context context, AppWidgetHost appWidgetHost,
             LayoutParserCallback callback, Resources res,
             int layoutId, String rootTag) {
+        this(context, appWidgetHost, callback, res, () -> res.getXml(layoutId), rootTag);
+    }
+
+    public AutoInstallsLayout(Context context, AppWidgetHost appWidgetHost,
+            LayoutParserCallback callback, Resources res,
+            Supplier<XmlPullParser> initialLayoutSupplier, String rootTag) {
         mContext = context;
         mAppWidgetHost = appWidgetHost;
         mCallback = callback;
@@ -180,7 +183,7 @@
         mRootTag = rootTag;
 
         mSourceRes = res;
-        mLayoutId = layoutId;
+        mInitialLayoutSupplier = initialLayoutSupplier;
 
         mIdp = LauncherAppState.getIDP(context);
         mRowCount = mIdp.numRows;
@@ -190,12 +193,12 @@
     /**
      * Loads the layout in the db and returns the number of entries added on the desktop.
      */
-    public int loadLayout(SQLiteDatabase db, ArrayList<Long> screenIds) {
+    public int loadLayout(SQLiteDatabase db, IntArray screenIds) {
         mDb = db;
         try {
-            return parseLayout(mLayoutId, screenIds);
+            return parseLayout(mInitialLayoutSupplier.get(), screenIds);
         } catch (Exception e) {
-            Log.e(TAG, "Error parsing layout: " + e);
+            Log.e(TAG, "Error parsing layout: ", e);
             return -1;
         }
     }
@@ -203,9 +206,8 @@
     /**
      * Parses the layout and returns the number of elements added on the homescreen.
      */
-    protected int parseLayout(int layoutId, ArrayList<Long> screenIds)
+    protected int parseLayout(XmlPullParser parser, IntArray screenIds)
             throws XmlPullParserException, IOException {
-        XmlResourceParser parser = mSourceRes.getXml(layoutId);
         beginDocument(parser, mRootTag);
         final int depth = parser.getDepth();
         int type;
@@ -226,16 +228,14 @@
      * Parses container and screenId attribute from the current tag, and puts it in the out.
      * @param out array of size 2.
      */
-    protected void parseContainerAndScreen(XmlResourceParser parser, long[] out) {
+    protected void parseContainerAndScreen(XmlPullParser parser, int[] out) {
         if (HOTSEAT_CONTAINER_NAME.equals(getAttributeValue(parser, ATTR_CONTAINER))) {
             out[0] = Favorites.CONTAINER_HOTSEAT;
             // Hack: hotseat items are stored using screen ids
-            long rank = Long.parseLong(getAttributeValue(parser, ATTR_RANK));
-            out[1] = (FeatureFlags.NO_ALL_APPS_ICON || rank < mIdp.getAllAppsButtonRank())
-                    ? rank : (rank + 1);
+            out[1] = Integer.parseInt(getAttributeValue(parser, ATTR_RANK));
         } else {
             out[0] = Favorites.CONTAINER_DESKTOP;
-            out[1] = Long.parseLong(getAttributeValue(parser, ATTR_SCREEN));
+            out[1] = Integer.parseInt(getAttributeValue(parser, ATTR_SCREEN));
         }
     }
 
@@ -243,16 +243,14 @@
      * Parses the current node and returns the number of elements added.
      */
     protected int parseAndAddNode(
-        XmlResourceParser parser,
-        ArrayMap<String, TagParser> tagParserMap,
-        ArrayList<Long> screenIds)
+            XmlPullParser parser, ArrayMap<String, TagParser> tagParserMap, IntArray screenIds)
         throws XmlPullParserException, IOException {
 
         if (TAG_INCLUDE.equals(parser.getName())) {
             final int resId = getAttributeResourceValue(parser, ATTR_WORKSPACE, 0);
             if (resId != 0) {
                 // recursively load some more favorites, why not?
-                return parseLayout(resId, screenIds);
+                return parseLayout(mSourceRes.getXml(resId), screenIds);
             } else {
                 return 0;
             }
@@ -260,8 +258,8 @@
 
         mValues.clear();
         parseContainerAndScreen(parser, mTemp);
-        final long container = mTemp[0];
-        final long screenId = mTemp[1];
+        final int container = mTemp[0];
+        final int screenId = mTemp[1];
 
         mValues.put(Favorites.CONTAINER, container);
         mValues.put(Favorites.SCREEN, screenId);
@@ -276,7 +274,7 @@
             if (LOGD) Log.d(TAG, "Ignoring unknown element tag: " + parser.getName());
             return 0;
         }
-        long newElementId = tagParser.parseAndAdd(parser);
+        int newElementId = tagParser.parseAndAdd(parser);
         if (newElementId >= 0) {
             // Keep track of the set of screens which need to be added to the db.
             if (!screenIds.contains(screenId) &&
@@ -288,8 +286,8 @@
         return 0;
     }
 
-    protected long addShortcut(String title, Intent intent, int type) {
-        long id = mCallback.generateNewItemId();
+    protected int addShortcut(String title, Intent intent, int type) {
+        int id = mCallback.generateNewItemId();
         mValues.put(Favorites.INTENT, intent.toUri(0));
         mValues.put(Favorites.TITLE, title);
         mValues.put(Favorites.ITEM_TYPE, type);
@@ -326,7 +324,7 @@
          * Parses the tag and adds to the db
          * @return the id of the row added or -1;
          */
-        long parseAndAdd(XmlResourceParser parser)
+        int parseAndAdd(XmlPullParser parser)
                 throws XmlPullParserException, IOException;
     }
 
@@ -336,7 +334,7 @@
     protected class AppShortcutParser implements TagParser {
 
         @Override
-        public long parseAndAdd(XmlResourceParser parser) {
+        public int parseAndAdd(XmlPullParser parser) {
             final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
             final String className = getAttributeValue(parser, ATTR_CLASS_NAME);
 
@@ -373,7 +371,7 @@
         /**
          * Helper method to allow extending the parser capabilities
          */
-        protected long invalidPackageOrClass(XmlResourceParser parser) {
+        protected int invalidPackageOrClass(XmlPullParser parser) {
             Log.w(TAG, "Skipping invalid <favorite> with no component");
             return -1;
         }
@@ -385,7 +383,7 @@
     protected class AutoInstallParser implements TagParser {
 
         @Override
-        public long parseAndAdd(XmlResourceParser parser) {
+        public int parseAndAdd(XmlPullParser parser) {
             final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
             final String className = getAttributeValue(parser, ATTR_CLASS_NAME);
             if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(className)) {
@@ -393,7 +391,7 @@
                 return -1;
             }
 
-            mValues.put(Favorites.RESTORED, ShortcutInfo.FLAG_AUTOINSTALL_ICON);
+            mValues.put(Favorites.RESTORED, WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON);
             final Intent intent = new Intent(Intent.ACTION_MAIN, null)
                 .addCategory(Intent.CATEGORY_LAUNCHER)
                 .setComponent(new ComponentName(packageName, className))
@@ -416,7 +414,7 @@
         }
 
         @Override
-        public long parseAndAdd(XmlResourceParser parser) {
+        public int parseAndAdd(XmlPullParser parser) {
             final int titleResId = getAttributeResourceValue(parser, ATTR_TITLE, 0);
             final int iconId = getAttributeResourceValue(parser, ATTR_ICON, 0);
 
@@ -438,7 +436,7 @@
 
             // Auto installs should always support the current platform version.
             LauncherIcons li = LauncherIcons.obtain(mContext);
-            mValues.put(LauncherSettings.Favorites.ICON, Utilities.flattenBitmap(
+            mValues.put(LauncherSettings.Favorites.ICON, GraphicsUtils.flattenBitmap(
                     li.createBadgedIconBitmap(icon, Process.myUserHandle(), VERSION.SDK_INT).icon));
             li.recycle();
 
@@ -451,7 +449,7 @@
                     intent, Favorites.ITEM_TYPE_SHORTCUT);
         }
 
-        protected Intent parseIntent(XmlResourceParser parser) {
+        protected Intent parseIntent(XmlPullParser parser) {
             final String url = getAttributeValue(parser, ATTR_URL);
             if (TextUtils.isEmpty(url) || !Patterns.WEB_URL.matcher(url).matches()) {
                 if (LOGD) Log.d(TAG, "Ignoring shortcut, invalid url: " + url);
@@ -472,7 +470,7 @@
     protected class PendingWidgetParser implements TagParser {
 
         @Override
-        public long parseAndAdd(XmlResourceParser parser)
+        public int parseAndAdd(XmlPullParser parser)
                 throws XmlPullParserException, IOException {
             final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
             final String className = getAttributeValue(parser, ATTR_CLASS_NAME);
@@ -511,7 +509,7 @@
             return verifyAndInsert(new ComponentName(packageName, className), extras);
         }
 
-        protected long verifyAndInsert(ComponentName cn, Bundle extras) {
+        protected int verifyAndInsert(ComponentName cn, Bundle extras) {
             mValues.put(Favorites.APPWIDGET_PROVIDER, cn.flattenToString());
             mValues.put(Favorites.RESTORED,
                     LauncherAppWidgetInfo.FLAG_ID_NOT_VALID |
@@ -522,7 +520,7 @@
                 mValues.put(Favorites.INTENT, new Intent().putExtras(extras).toUri(0));
             }
 
-            long insertedId = mCallback.insertAndCheck(mDb, mValues);
+            int insertedId = mCallback.insertAndCheck(mDb, mValues);
             if (insertedId < 0) {
                 return -1;
             } else {
@@ -543,7 +541,7 @@
         }
 
         @Override
-        public long parseAndAdd(XmlResourceParser parser)
+        public int parseAndAdd(XmlPullParser parser)
                 throws XmlPullParserException, IOException {
             final String title;
             final int titleResId = getAttributeResourceValue(parser, ATTR_TITLE, 0);
@@ -558,14 +556,14 @@
             mValues.put(Favorites.SPANX, 1);
             mValues.put(Favorites.SPANY, 1);
             mValues.put(Favorites._ID, mCallback.generateNewItemId());
-            long folderId = mCallback.insertAndCheck(mDb, mValues);
+            int folderId = mCallback.insertAndCheck(mDb, mValues);
             if (folderId < 0) {
                 if (LOGD) Log.e(TAG, "Unable to add folder");
                 return -1;
             }
 
             final ContentValues myValues = new ContentValues(mValues);
-            ArrayList<Long> folderItems = new ArrayList<>();
+            IntArray folderItems = new IntArray();
 
             int type;
             int folderDepth = parser.getDepth();
@@ -581,7 +579,7 @@
 
                 TagParser tagParser = mFolderElements.get(parser.getName());
                 if (tagParser != null) {
-                    final long id = tagParser.parseAndAdd(parser);
+                    final int id = tagParser.parseAndAdd(parser);
                     if (id >= 0) {
                         folderItems.add(id);
                         rank++;
@@ -591,7 +589,7 @@
                 }
             }
 
-            long addedId = folderId;
+            int addedId = folderId;
 
             // We can only have folders with >= 2 items, so we need to remove the
             // folder and clean up if less than 2 items were included, or some
@@ -651,7 +649,7 @@
      * Return attribute value, attempting launcher-specific namespace first
      * before falling back to anonymous attribute.
      */
-    protected static String getAttributeValue(XmlResourceParser parser, String attribute) {
+    protected static String getAttributeValue(XmlPullParser parser, String attribute) {
         String value = parser.getAttributeValue(
                 "http://schemas.android.com/apk/res-auto/com.android.launcher3", attribute);
         if (value == null) {
@@ -664,21 +662,22 @@
      * Return attribute resource value, attempting launcher-specific namespace
      * first before falling back to anonymous attribute.
      */
-    protected static int getAttributeResourceValue(XmlResourceParser parser, String attribute,
+    protected static int getAttributeResourceValue(XmlPullParser parser, String attribute,
             int defaultValue) {
-        int value = parser.getAttributeResourceValue(
+        AttributeSet attrs = Xml.asAttributeSet(parser);
+        int value = attrs.getAttributeResourceValue(
                 "http://schemas.android.com/apk/res-auto/com.android.launcher3", attribute,
                 defaultValue);
         if (value == defaultValue) {
-            value = parser.getAttributeResourceValue(null, attribute, defaultValue);
+            value = attrs.getAttributeResourceValue(null, attribute, defaultValue);
         }
         return value;
     }
 
     public interface LayoutParserCallback {
-        long generateNewItemId();
+        int generateNewItemId();
 
-        long insertAndCheck(SQLiteDatabase db, ContentValues values);
+        int insertAndCheck(SQLiteDatabase db, ContentValues values);
     }
 
     @Thunk static void copyInteger(ContentValues from, ContentValues to, String key) {
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index a4b6f5b..6455056 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -25,22 +25,29 @@
 import android.content.ContextWrapper;
 import android.content.Intent;
 import android.content.res.Configuration;
-import android.support.annotation.IntDef;
-import android.view.View.AccessibilityDelegate;
+import android.view.ContextThemeWrapper;
 
 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
+import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.logging.StatsLogUtils;
+import com.android.launcher3.logging.StatsLogUtils.LogStateProvider;
 import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.logging.UserEventDispatcher.UserEventDelegate;
 import com.android.launcher3.uioverrides.UiFactory;
 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 java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.util.ArrayList;
 
-public abstract class BaseActivity extends Activity implements UserEventDelegate{
+import androidx.annotation.IntDef;
+
+public abstract class BaseActivity extends Activity
+        implements UserEventDelegate, LogStateProvider, ActivityContext {
 
     public static final int INVISIBLE_BY_STATE_HANDLER = 1 << 0;
     public static final int INVISIBLE_BY_APP_TRANSITIONS = 1 << 1;
@@ -71,6 +78,7 @@
 
     protected DeviceProfile mDeviceProfile;
     protected UserEventDispatcher mUserEventDispatcher;
+    protected StatsLogManager mStatsLogManager;
     protected SystemUiController mSystemUiController;
 
     private static final int ACTIVITY_STATE_STARTED = 1 << 0;
@@ -95,34 +103,35 @@
     // animation
     @InvisibilityFlags private int mForceInvisible;
 
+    private final ViewCache mViewCache = new ViewCache();
+
+    public ViewCache getViewCache() {
+        return mViewCache;
+    }
+
+    @Override
     public DeviceProfile getDeviceProfile() {
         return mDeviceProfile;
     }
 
-    public AccessibilityDelegate getAccessibilityDelegate() {
-        return null;
-    }
+    public int getCurrentState() { return StatsLogUtils.LAUNCHER_STATE_BACKGROUND; }
 
     public void modifyUserEvent(LauncherLogProto.LauncherEvent event) {}
 
+    public final StatsLogManager getStatsLogManager() {
+        if (mStatsLogManager == null) {
+            mStatsLogManager = StatsLogManager.newInstance(this, this);
+        }
+        return mStatsLogManager;
+    }
+
     public final UserEventDispatcher getUserEventDispatcher() {
         if (mUserEventDispatcher == null) {
-            mUserEventDispatcher = UserEventDispatcher.newInstance(this, mDeviceProfile, this);
+            mUserEventDispatcher = UserEventDispatcher.newInstance(this, this);
         }
         return mUserEventDispatcher;
     }
 
-    public boolean isInMultiWindowModeCompat() {
-        return Utilities.ATLEAST_NOUGAT && isInMultiWindowMode();
-    }
-
-    public static BaseActivity fromContext(Context context) {
-        if (context instanceof BaseActivity) {
-            return (BaseActivity) context;
-        }
-        return ((BaseActivity) ((ContextWrapper) context).getBaseContext());
-    }
-
     public SystemUiController getSystemUiController() {
         if (mSystemUiController == null) {
             mSystemUiController = new SystemUiController(getWindow());
@@ -166,6 +175,10 @@
         mActivityFlags &= ~ACTIVITY_STATE_STARTED & ~ACTIVITY_STATE_USER_ACTIVE;
         mForceInvisible = 0;
         super.onStop();
+
+        // 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);
     }
 
     @Override
@@ -220,7 +233,7 @@
     /**
      * Used to set the override visibility state, used only to handle the transition home with the
      * recents animation.
-     * @see LauncherAppTransitionManagerImpl#getWallpaperOpenRunner()
+     * @see QuickstepAppTransitionManagerImpl#getWallpaperOpenRunner()
      */
     public void addForceInvisibleFlag(@InvisibilityFlags int flag) {
         mForceInvisible |= flag;
@@ -259,4 +272,14 @@
         writer.println(" mActivityFlags: " + mActivityFlags);
         writer.println(" mForceInvisible: " + mForceInvisible);
     }
+
+    public static <T extends BaseActivity> T fromContext(Context context) {
+        if (context instanceof BaseActivity) {
+            return (T) context;
+        } else if (context instanceof ContextThemeWrapper) {
+            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 eec196e..f69b172 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -18,9 +18,8 @@
 
 import android.app.ActivityOptions;
 import android.content.ActivityNotFoundException;
-import android.content.Context;
-import android.content.ContextWrapper;
 import android.content.Intent;
+import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.Process;
@@ -28,17 +27,19 @@
 import android.os.UserHandle;
 import android.util.Log;
 import android.view.ActionMode;
-import android.view.Surface;
 import android.view.View;
 import android.widget.Toast;
 
 import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.badge.BadgeInfo;
 import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.model.AppLaunchTracker;
+import com.android.launcher3.shortcuts.DeepShortcutManager;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.uioverrides.DisplayRotationListener;
 import com.android.launcher3.uioverrides.WallpaperColorInfo;
-import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.views.BaseDragLayer;
+import com.android.launcher3.util.Themes;
+
+import androidx.annotation.Nullable;
 
 /**
  * Extension of BaseActivity allowing support for drag-n-drop
@@ -48,10 +49,6 @@
 
     private static final String TAG = "BaseDraggingActivity";
 
-    // The Intent extra that defines whether to ignore the launch animation
-    public static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION =
-            "com.android.launcher3.intent.extra.shortcut.INGORE_LAUNCH_ANIMATION";
-
     // When starting an action mode, setting this tag will cause the action mode to be cancelled
     // automatically when user interacts with the launcher.
     public static final Object AUTO_CANCEL_ACTION_MODE = new Object();
@@ -74,7 +71,7 @@
         // Update theme
         WallpaperColorInfo wallpaperColorInfo = WallpaperColorInfo.getInstance(this);
         wallpaperColorInfo.addOnChangeListener(this);
-        int themeRes = getThemeRes(wallpaperColorInfo);
+        int themeRes = Themes.getActivityThemeRes(this);
         if (themeRes != mThemeRes) {
             mThemeRes = themeRes;
             setTheme(themeRes);
@@ -83,18 +80,18 @@
 
     @Override
     public void onExtractedColorsChanged(WallpaperColorInfo wallpaperColorInfo) {
-        if (mThemeRes != getThemeRes(wallpaperColorInfo)) {
-            recreate();
-        }
+        updateTheme();
     }
 
-    protected int getThemeRes(WallpaperColorInfo wallpaperColorInfo) {
-        if (wallpaperColorInfo.isDark()) {
-            return wallpaperColorInfo.supportsDarkText() ?
-                    R.style.AppTheme_Dark_DarkText : R.style.AppTheme_Dark;
-        } else {
-            return wallpaperColorInfo.supportsDarkText() ?
-                    R.style.AppTheme_DarkText : R.style.AppTheme;
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        updateTheme();
+    }
+
+    private void updateTheme() {
+        if (mThemeRes != Themes.getActivityThemeRes(this)) {
+            recreate();
         }
     }
 
@@ -110,6 +107,7 @@
         mCurrentActionMode = null;
     }
 
+    @Override
     public boolean finishAutoCancelActionMode() {
         if (mCurrentActionMode != null && AUTO_CANCEL_ACTION_MODE == mCurrentActionMode.getTag()) {
             mCurrentActionMode.finish();
@@ -118,23 +116,10 @@
         return false;
     }
 
-    public abstract BaseDragLayer getDragLayer();
-
     public abstract <T extends View> T getOverviewPanel();
 
     public abstract View getRootView();
 
-    public abstract BadgeInfo getBadgeInfoForItem(ItemInfo info);
-
-    public abstract void invalidateParent(ItemInfo info);
-
-    public static BaseDraggingActivity fromContext(Context context) {
-        if (context instanceof BaseDraggingActivity) {
-            return (BaseDraggingActivity) context;
-        }
-        return ((BaseDraggingActivity) ((ContextWrapper) context).getBaseContext());
-    }
-
     public Rect getViewBounds(View v) {
         int[] pos = new int[2];
         v.getLocationOnScreen(pos);
@@ -148,20 +133,18 @@
 
     public abstract ActivityOptions getActivityLaunchOptions(View v);
 
-    public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
+    public boolean startActivitySafely(View v, Intent intent, @Nullable ItemInfo item,
+            @Nullable String sourceContainer) {
+        if (TestProtocol.sDebugTracing) {
+            android.util.Log.d(TestProtocol.NO_START_TAG,
+                    "startActivitySafely 1");
+        }
         if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) {
             Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
             return false;
         }
 
-        // Only launch using the new animation if the shortcut has not opted out (this is a
-        // private contract between launcher and may be ignored in the future).
-        boolean useLaunchAnimation = (v != null) &&
-                !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
-        Bundle optsBundle = useLaunchAnimation
-                ? getActivityLaunchOptionsAsBundle(v)
-                : null;
-
+        Bundle optsBundle = (v != null) ? getActivityLaunchOptionsAsBundle(v) : null;
         UserHandle user = item == null ? null : item.user;
 
         // Prepare intent
@@ -170,22 +153,30 @@
             intent.setSourceBounds(getViewBounds(v));
         }
         try {
-            boolean isShortcut = Utilities.ATLEAST_MARSHMALLOW
-                    && (item instanceof ShortcutInfo)
+            boolean isShortcut = (item instanceof WorkspaceItemInfo)
                     && (item.itemType == Favorites.ITEM_TYPE_SHORTCUT
                     || item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
-                    && !((ShortcutInfo) item).isPromise();
+                    && !((WorkspaceItemInfo) item).isPromise();
             if (isShortcut) {
                 // Shortcuts need some special checks due to legacy reasons.
-                startShortcutIntentSafely(intent, optsBundle, item);
+                startShortcutIntentSafely(intent, optsBundle, item, sourceContainer);
             } else if (user == null || user.equals(Process.myUserHandle())) {
                 // Could be launching some bookkeeping activity
+                if (TestProtocol.sDebugTracing) {
+                    android.util.Log.d(TestProtocol.NO_START_TAG,
+                            "startActivitySafely 2");
+                }
                 startActivity(intent, optsBundle);
+                AppLaunchTracker.INSTANCE.get(this).onStartApp(intent.getComponent(),
+                        Process.myUserHandle(), sourceContainer);
             } else {
                 LauncherAppsCompat.getInstance(this).startActivityForProfile(
                         intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
+                AppLaunchTracker.INSTANCE.get(this).onStartApp(intent.getComponent(), user,
+                        sourceContainer);
             }
             getUserEventDispatcher().logAppLaunch(v, intent);
+            getStatsLogManager().logAppLaunch(v, intent);
             return true;
         } catch (ActivityNotFoundException|SecurityException e) {
             Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
@@ -194,7 +185,8 @@
         return false;
     }
 
-    private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) {
+    private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info,
+            @Nullable String sourceContainer) {
         try {
             StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy();
             try {
@@ -205,10 +197,12 @@
                         .penaltyLog().build());
 
                 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
-                    String id = ((ShortcutInfo) info).getDeepShortcutId();
+                    String id = ((WorkspaceItemInfo) info).getDeepShortcutId();
                     String packageName = intent.getPackage();
                     DeepShortcutManager.getInstance(this).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);
diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java
index 74b9cfa..c84be4d 100644
--- a/src/com/android/launcher3/BaseRecyclerView.java
+++ b/src/com/android/launcher3/BaseRecyclerView.java
@@ -17,7 +17,6 @@
 package com.android.launcher3;
 
 import android.content.Context;
-import android.support.v7.widget.RecyclerView;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.View;
@@ -25,6 +24,8 @@
 
 import com.android.launcher3.views.RecyclerViewFastScroller;
 
+import androidx.recyclerview.widget.RecyclerView;
+
 
 /**
  * A base {@link RecyclerView}, which does the following:
@@ -122,12 +123,12 @@
      * @param ev MotionEvent in {@param eventSource}
      */
     public boolean shouldContainerScroll(MotionEvent ev, View eventSource) {
-        int[] point = new int[2];
-        point[0] = (int) ev.getX();
-        point[1] = (int) ev.getY();
+        float[] point = new float[2];
+        point[0] = ev.getX();
+        point[1] = ev.getY();
         Utilities.mapCoordInSelfToDescendant(mScrollbar, eventSource, point);
         // IF the MotionEvent is inside the thumb, container should not be pulled down.
-        if (mScrollbar.shouldBlockIntercept(point[0], point[1])) {
+        if (mScrollbar.shouldBlockIntercept((int) point[0], (int) point[1])) {
             return false;
         }
 
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index fb7c0ce..1619e36 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -16,6 +16,10 @@
 
 package com.android.launcher3;
 
+import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.content.res.ColorStateList;
@@ -23,13 +27,12 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
-import android.support.v4.graphics.ColorUtils;
 import android.text.TextUtils.TruncateAt;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.util.Property;
 import android.util.TypedValue;
 import android.view.KeyEvent;
@@ -39,16 +42,20 @@
 import android.view.ViewDebug;
 import android.widget.TextView;
 
-import com.android.launcher3.IconCache.IconLoadRequest;
-import com.android.launcher3.IconCache.ItemInfoUpdateReceiver;
 import com.android.launcher3.Launcher.OnResumeCallback;
-import com.android.launcher3.badge.BadgeInfo;
-import com.android.launcher3.badge.BadgeRenderer;
+import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
+import com.android.launcher3.dot.DotInfo;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.graphics.DrawableFactory;
 import com.android.launcher3.graphics.IconPalette;
+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.IconCache.ItemInfoUpdateReceiver;
 import com.android.launcher3.model.PackageItemInfo;
+import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.views.ActivityContext;
 
 import java.text.NumberFormat;
 
@@ -66,16 +73,16 @@
     private static final int[] STATE_PRESSED = new int[] {android.R.attr.state_pressed};
 
 
-    private static final Property<BubbleTextView, Float> BADGE_SCALE_PROPERTY
-            = new Property<BubbleTextView, Float>(Float.TYPE, "badgeScale") {
+    private static final Property<BubbleTextView, Float> DOT_SCALE_PROPERTY
+            = new Property<BubbleTextView, Float>(Float.TYPE, "dotScale") {
         @Override
         public Float get(BubbleTextView bubbleTextView) {
-            return bubbleTextView.mBadgeScale;
+            return bubbleTextView.mDotParams.scale;
         }
 
         @Override
         public void set(BubbleTextView bubbleTextView, Float value) {
-            bubbleTextView.mBadgeScale = value;
+            bubbleTextView.mDotParams.scale = value;
             bubbleTextView.invalidate();
         }
     };
@@ -93,7 +100,7 @@
         }
     };
 
-    private final BaseDraggingActivity mActivity;
+    private final ActivityContext mActivity;
     private Drawable mIcon;
     private final boolean mCenterVertically;
 
@@ -111,13 +118,13 @@
     @ViewDebug.ExportedProperty(category = "launcher")
     private float mTextAlpha = 1;
 
-    private BadgeInfo mBadgeInfo;
-    private BadgeRenderer mBadgeRenderer;
-    private int mBadgeColor;
-    private float mBadgeScale;
-    private boolean mForceHideBadge;
-    private Point mTempSpaceForBadgeOffset = new Point();
-    private Rect mTempIconBounds = new Rect();
+    @ViewDebug.ExportedProperty(category = "launcher")
+    private DotInfo mDotInfo;
+    private DotRenderer mDotRenderer;
+    @ViewDebug.ExportedProperty(category = "launcher", deepExport = true)
+    private DotRenderer.DrawParams mDotParams;
+    private Animator mDotScaleAnim;
+    private boolean mForceHideDot;
 
     @ViewDebug.ExportedProperty(category = "launcher")
     private boolean mStayPressed;
@@ -138,8 +145,7 @@
 
     public BubbleTextView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
-        mActivity = BaseDraggingActivity.fromContext(context);
-        DeviceProfile grid = mActivity.getDeviceProfile();
+        mActivity = ActivityContext.lookupContext(context);
         mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
 
         TypedArray a = context.obtainStyledAttributes(attrs,
@@ -147,18 +153,24 @@
         mLayoutHorizontal = a.getBoolean(R.styleable.BubbleTextView_layoutHorizontal, false);
 
         int display = a.getInteger(R.styleable.BubbleTextView_iconDisplay, DISPLAY_WORKSPACE);
-        int defaultIconSize = grid.iconSizePx;
+        final int defaultIconSize;
         if (display == DISPLAY_WORKSPACE) {
+            DeviceProfile grid = mActivity.getWallpaperDeviceProfile();
             setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
             setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
+            defaultIconSize = grid.iconSizePx;
         } else if (display == DISPLAY_ALL_APPS) {
+            DeviceProfile grid = mActivity.getDeviceProfile();
             setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.allAppsIconTextSizePx);
             setCompoundDrawablePadding(grid.allAppsIconDrawablePaddingPx);
             defaultIconSize = grid.allAppsIconSizePx;
         } else if (display == DISPLAY_FOLDER) {
+            DeviceProfile grid = mActivity.getDeviceProfile();
             setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.folderChildTextSizePx);
             setCompoundDrawablePadding(grid.folderChildDrawablePaddingPx);
             defaultIconSize = grid.folderChildIconSizePx;
+        } else {
+            defaultIconSize = mActivity.getDeviceProfile().iconSizePx;
         }
         mCenterVertically = a.getBoolean(R.styleable.BubbleTextView_centerVertically, false);
 
@@ -169,6 +181,8 @@
         mLongPressHelper = new CheckLongPressHelper(this);
         mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
 
+        mDotParams = new DotRenderer.DrawParams();
+
         setEllipsize(TruncateAt.END);
         setAccessibilityDelegate(mActivity.getAccessibilityDelegate());
         setTextAlpha(1f);
@@ -185,30 +199,61 @@
      * Resets the view so it can be recycled.
      */
     public void reset() {
-        mBadgeInfo = null;
-        mBadgeColor = Color.TRANSPARENT;
-        mBadgeScale = 0f;
-        mForceHideBadge = false;
+        mDotInfo = null;
+        mDotParams.color = Color.TRANSPARENT;
+        cancelDotScaleAnim();
+        mDotParams.scale = 0f;
+        mForceHideDot = false;
     }
 
-    public void applyFromShortcutInfo(ShortcutInfo info) {
-        applyFromShortcutInfo(info, false);
+    private void cancelDotScaleAnim() {
+        if (mDotScaleAnim != null) {
+            mDotScaleAnim.cancel();
+        }
     }
 
-    public void applyFromShortcutInfo(ShortcutInfo info, boolean promiseStateChanged) {
+    private void animateDotScale(float... dotScales) {
+        cancelDotScaleAnim();
+        mDotScaleAnim = ObjectAnimator.ofFloat(this, DOT_SCALE_PROPERTY, dotScales);
+        mDotScaleAnim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mDotScaleAnim = null;
+            }
+        });
+        mDotScaleAnim.start();
+    }
+
+    public void applyFromWorkspaceItem(WorkspaceItemInfo info) {
+        applyFromWorkspaceItem(info, false);
+    }
+
+    @Override
+    public void setAccessibilityDelegate(AccessibilityDelegate delegate) {
+        if (delegate instanceof LauncherAccessibilityDelegate) {
+            super.setAccessibilityDelegate(delegate);
+        } else {
+            // NO-OP
+            // Workaround for b/129745295 where RecyclerView is setting our Accessibility
+            // delegate incorrectly. There are no cases when we shouldn't be using the
+            // LauncherAccessibilityDelegate for BubbleTextView.
+        }
+    }
+
+    public void applyFromWorkspaceItem(WorkspaceItemInfo info, boolean promiseStateChanged) {
         applyIconAndLabel(info);
         setTag(info);
         if (promiseStateChanged || (info.hasPromiseIconUi())) {
             applyPromiseState(promiseStateChanged);
         }
 
-        applyBadgeState(info, false /* animate */);
+        applyDotState(info, false /* animate */);
     }
 
     public void applyFromApplicationInfo(AppInfo info) {
         applyIconAndLabel(info);
 
-        // We don't need to check the info since it's not a ShortcutInfo
+        // We don't need to check the info since it's not a WorkspaceItemInfo
         super.setTag(info);
 
         // Verify high res immediately
@@ -218,12 +263,12 @@
             PromiseAppInfo promiseAppInfo = (PromiseAppInfo) info;
             applyProgressLevel(promiseAppInfo.level);
         }
-        applyBadgeState(info, false /* animate */);
+        applyDotState(info, false /* animate */);
     }
 
     public void applyFromPackageItemInfo(PackageItemInfo info) {
         applyIconAndLabel(info);
-        // We don't need to check the info since it's not a ShortcutInfo
+        // We don't need to check the info since it's not a WorkspaceItemInfo
         super.setTag(info);
 
         // Verify high res immediately
@@ -231,8 +276,9 @@
     }
 
     private void applyIconAndLabel(ItemInfoWithIcon info) {
-        FastBitmapDrawable iconDrawable = DrawableFactory.get(getContext()).newIcon(info);
-        mBadgeColor = IconPalette.getMutedColor(info.iconColor, 0.54f);
+        FastBitmapDrawable iconDrawable = DrawableFactory.INSTANCE.get(getContext())
+                .newIcon(getContext(), info);
+        mDotParams.color = IconPalette.getMutedColor(info.iconColor, 0.54f);
 
         setIcon(iconDrawable);
         setText(info.title);
@@ -246,16 +292,8 @@
     /**
      * Overrides the default long press timeout.
      */
-    public void setLongPressTimeout(int longPressTimeout) {
-        mLongPressHelper.setLongPressTimeout(longPressTimeout);
-    }
-
-    @Override
-    public void setTag(Object tag) {
-        if (tag != null) {
-            LauncherModel.checkItemInfo((ItemInfo) tag);
-        }
-        super.setTag(tag);
+    public void setLongPressTimeoutFactor(float longPressTimeoutFactor) {
+        mLongPressHelper.setLongPressTimeoutFactor(longPressTimeoutFactor);
     }
 
     @Override
@@ -281,6 +319,9 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_START_TAG, "BubbleTextView.onTouchEvent " + event);
+        }
         // Call the superclass onTouchEvent first, because sometimes it changes the state to
         // isPressed() on an ACTION_UP
         boolean result = super.onTouchEvent(event);
@@ -317,6 +358,14 @@
     }
 
     @Override
+    public void onVisibilityAggregated(boolean isVisible) {
+        super.onVisibilityAggregated(isVisible);
+        if (mIcon != null) {
+            mIcon.setVisible(isVisible, false);
+        }
+    }
+
+    @Override
     public void onLauncherResume() {
         // Reset the pressed state of icon that was locked in the press state while activity
         // was launching
@@ -341,55 +390,58 @@
     }
 
     @SuppressWarnings("wrongcall")
-    protected void drawWithoutBadge(Canvas canvas) {
+    protected void drawWithoutDot(Canvas canvas) {
         super.onDraw(canvas);
     }
 
     @Override
     public void onDraw(Canvas canvas) {
         super.onDraw(canvas);
-        drawBadgeIfNecessary(canvas);
+        drawDotIfNecessary(canvas);
     }
 
     /**
-     * Draws the icon badge in the top right corner of the icon bounds.
+     * Draws the notification dot in the top right corner of the icon bounds.
      * @param canvas The canvas to draw to.
      */
-    protected void drawBadgeIfNecessary(Canvas canvas) {
-        if (!mForceHideBadge && (hasBadge() || mBadgeScale > 0)) {
-            getIconBounds(mTempIconBounds);
-            mTempSpaceForBadgeOffset.set((getWidth() - mIconSize) / 2, getPaddingTop());
+    protected void drawDotIfNecessary(Canvas canvas) {
+        if (!mForceHideDot && (hasDot() || mDotParams.scale > 0)) {
+            getIconBounds(mDotParams.iconBounds);
+            Utilities.scaleRectAboutCenter(mDotParams.iconBounds, IconShape.getNormalizationScale());
             final int scrollX = getScrollX();
             final int scrollY = getScrollY();
             canvas.translate(scrollX, scrollY);
-            mBadgeRenderer.draw(canvas, mBadgeColor, mTempIconBounds, mBadgeScale,
-                    mTempSpaceForBadgeOffset);
+            mDotRenderer.draw(canvas, mDotParams);
             canvas.translate(-scrollX, -scrollY);
         }
     }
 
-    public void forceHideBadge(boolean forceHideBadge) {
-        if (mForceHideBadge == forceHideBadge) {
+    public void forceHideDot(boolean forceHideDot) {
+        if (mForceHideDot == forceHideDot) {
             return;
         }
-        mForceHideBadge = forceHideBadge;
+        mForceHideDot = forceHideDot;
 
-        if (forceHideBadge) {
+        if (forceHideDot) {
             invalidate();
-        } else if (hasBadge()) {
-            ObjectAnimator.ofFloat(this, BADGE_SCALE_PROPERTY, 0, 1).start();
+        } else if (hasDot()) {
+            animateDotScale(0, 1);
         }
     }
 
-    private boolean hasBadge() {
-        return mBadgeInfo != null;
+    private boolean hasDot() {
+        return mDotInfo != null;
     }
 
     public void getIconBounds(Rect outBounds) {
-        int top = getPaddingTop();
-        int left = (getWidth() - mIconSize) / 2;
-        int right = left + mIconSize;
-        int bottom = top + mIconSize;
+        getIconBounds(this, outBounds, mIconSize);
+    }
+
+    public static void getIconBounds(View iconView, Rect outBounds, int iconSize) {
+        int top = iconView.getPaddingTop();
+        int left = (iconView.getWidth() - iconSize) / 2;
+        int right = left + iconSize;
+        int bottom = top + iconSize;
         outBounds.set(left, top, right, bottom);
     }
 
@@ -443,8 +495,7 @@
             // Special case to prevent text shadows in high contrast mode
             return Color.TRANSPARENT;
         }
-        return ColorUtils.setAlphaComponent(
-                mTextColor, Math.round(Color.alpha(mTextColor) * mTextAlpha));
+        return setColorAlphaBound(mTextColor, Math.round(Color.alpha(mTextColor) * mTextAlpha));
     }
 
     /**
@@ -464,11 +515,11 @@
     }
 
     public void applyPromiseState(boolean promiseStateChanged) {
-        if (getTag() instanceof ShortcutInfo) {
-            ShortcutInfo info = (ShortcutInfo) getTag();
+        if (getTag() instanceof WorkspaceItemInfo) {
+            WorkspaceItemInfo info = (WorkspaceItemInfo) getTag();
             final boolean isPromise = info.hasPromiseIconUi();
             final int progressLevel = isPromise ?
-                    ((info.hasStatusFlag(ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE) ?
+                    ((info.hasStatusFlag(WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE) ?
                             info.getInstallProgress() : 0)) : 100;
 
             PreloadIconDrawable preloadDrawable = applyProgressLevel(progressLevel);
@@ -498,8 +549,8 @@
                     preloadDrawable = (PreloadIconDrawable) mIcon;
                     preloadDrawable.setLevel(progressLevel);
                 } else {
-                    preloadDrawable = DrawableFactory.get(getContext())
-                            .newPendingIcon(info, getContext());
+                    preloadDrawable = DrawableFactory.INSTANCE.get(getContext())
+                            .newPendingIcon(getContext(), info);
                     preloadDrawable.setLevel(progressLevel);
                     setIcon(preloadDrawable);
                 }
@@ -509,27 +560,31 @@
         return null;
     }
 
-    public void applyBadgeState(ItemInfo itemInfo, boolean animate) {
+    public void applyDotState(ItemInfo itemInfo, boolean animate) {
         if (mIcon instanceof FastBitmapDrawable) {
-            boolean wasBadged = mBadgeInfo != null;
-            mBadgeInfo = mActivity.getBadgeInfoForItem(itemInfo);
-            boolean isBadged = mBadgeInfo != null;
-            float newBadgeScale = isBadged ? 1f : 0;
-            mBadgeRenderer = mActivity.getDeviceProfile().mBadgeRenderer;
-            if (wasBadged || isBadged) {
-                // Animate when a badge is first added or when it is removed.
-                if (animate && (wasBadged ^ isBadged) && isShown()) {
-                    ObjectAnimator.ofFloat(this, BADGE_SCALE_PROPERTY, newBadgeScale).start();
+            boolean wasDotted = mDotInfo != null;
+            mDotInfo = mActivity.getDotInfoForItem(itemInfo);
+            boolean isDotted = mDotInfo != null;
+            float newDotScale = isDotted ? 1f : 0;
+            mDotRenderer = mActivity.getDeviceProfile().mDotRenderer;
+            if (wasDotted || isDotted) {
+                // Animate when a dot is first added or when it is removed.
+                if (animate && (wasDotted ^ isDotted) && isShown()) {
+                    animateDotScale(newDotScale);
                 } else {
-                    mBadgeScale = newBadgeScale;
+                    cancelDotScaleAnim();
+                    mDotParams.scale = newDotScale;
                     invalidate();
                 }
             }
             if (itemInfo.contentDescription != null) {
-                if (hasBadge()) {
-                    int count = mBadgeInfo.getNotificationCount();
+                if (itemInfo.isDisabled()) {
+                    setContentDescription(getContext().getString(R.string.disabled_app_label,
+                            itemInfo.contentDescription));
+                } else if (hasDot()) {
+                    int count = mDotInfo.getNotificationCount();
                     setContentDescription(getContext().getResources().getQuantityString(
-                            R.plurals.badged_app_label, count, itemInfo.contentDescription, count));
+                            R.plurals.dotted_app_label, count, itemInfo.contentDescription, count));
                 } else {
                     setContentDescription(itemInfo.contentDescription);
                 }
@@ -545,6 +600,9 @@
             applyCompoundDrawables(icon);
         }
         mIcon = icon;
+        if (mIcon != null) {
+            mIcon.setVisible(getWindowVisibility() == VISIBLE && isShown(), false);
+        }
     }
 
     public void setIconVisible(boolean visible) {
@@ -588,8 +646,8 @@
 
             if (info instanceof AppInfo) {
                 applyFromApplicationInfo((AppInfo) info);
-            } else if (info instanceof ShortcutInfo) {
-                applyFromShortcutInfo((ShortcutInfo) info);
+            } else if (info instanceof WorkspaceItemInfo) {
+                applyFromWorkspaceItem((WorkspaceItemInfo) info);
                 mActivity.invalidateParent(info);
             } else if (info instanceof PackageItemInfo) {
                 applyFromPackageItemInfo((PackageItemInfo) info);
@@ -609,7 +667,7 @@
         }
         if (getTag() instanceof ItemInfoWithIcon) {
             ItemInfoWithIcon info = (ItemInfoWithIcon) getTag();
-            if (info.usingLowResIcon) {
+            if (info.usingLowResIcon()) {
                 mIconLoadRequest = LauncherAppState.getInstance(getContext()).getIconCache()
                         .updateIconInBackground(BubbleTextView.this, info);
             }
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index ed8c42d..2b0da43 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -33,6 +33,7 @@
 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;
@@ -55,6 +56,20 @@
 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;
 
@@ -206,7 +221,7 @@
         });
 
         mCurrentColorAnim.play(anim1);
-        mCurrentColorAnim.play(ObjectAnimator.ofArgb(this, "textColor", targetColor));
+        mCurrentColorAnim.play(ObjectAnimator.ofArgb(this, TEXT_COLOR, targetColor));
         mCurrentColorAnim.start();
     }
 
@@ -264,6 +279,10 @@
      */
     @Override
     public void onDrop(final DragObject d, final DragOptions options) {
+        if (options.isFlingToDelete) {
+            // FlingAnimation handles the animation and then calls completeDrop().
+            return;
+        }
         final DragLayer dragLayer = mLauncher.getDragLayer();
         final Rect from = new Rect();
         dragLayer.getViewRectRelativeToSelf(d.dragView, from);
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 6c2fd8e..09fb244 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -16,8 +16,11 @@
 
 package com.android.launcher3;
 
+import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
@@ -34,11 +37,10 @@
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Parcelable;
-import android.support.annotation.IntDef;
-import android.support.v4.view.ViewCompat;
 import android.util.ArrayMap;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.Property;
 import android.util.SparseArray;
 import android.view.MotionEvent;
 import android.view.View;
@@ -46,6 +48,9 @@
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 
+import androidx.annotation.IntDef;
+import androidx.core.view.ViewCompat;
+
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate;
 import com.android.launcher3.accessibility.FolderAccessibilityHelper;
@@ -55,11 +60,14 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.folder.PreviewBackground;
 import com.android.launcher3.graphics.DragPreviewProvider;
+import com.android.launcher3.graphics.RotationMode;
 import com.android.launcher3.util.CellAndSpan;
 import com.android.launcher3.util.GridOccupancy;
 import com.android.launcher3.util.ParcelableSparseArray;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.Thunk;
+import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.views.Transposable;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
 
 import java.lang.annotation.Retention;
@@ -70,14 +78,14 @@
 import java.util.Comparator;
 import java.util.Stack;
 
-public class CellLayout extends ViewGroup {
+public class CellLayout extends ViewGroup implements Transposable {
     public static final int WORKSPACE_ACCESSIBILITY_DRAG = 2;
     public static final int FOLDER_ACCESSIBILITY_DRAG = 1;
 
     private static final String TAG = "CellLayout";
     private static final boolean LOGD = false;
 
-    private final Launcher mLauncher;
+    protected final ActivityContext mActivity;
     @ViewDebug.ExportedProperty(category = "launcher")
     @Thunk int mCellWidth;
     @ViewDebug.ExportedProperty(category = "launcher")
@@ -101,7 +109,6 @@
     private GridOccupancy mTmpOccupied;
 
     private OnTouchListener mInterceptTouchListener;
-    private final StylusEventHelper mStylusEventHelper;
 
     private final ArrayList<PreviewBackground> mFolderBackgrounds = new ArrayList<>();
     final PreviewBackground mFolderLeaveBehind = new PreviewBackground();
@@ -177,6 +184,7 @@
     // Related to accessible drag and drop
     private DragAndDropAccessibilityDelegate mTouchHelper;
     private boolean mUseTouchHelper = false;
+    private RotationMode mRotationMode = RotationMode.NORMAL;
 
     public CellLayout(Context context) {
         this(context, null);
@@ -196,9 +204,9 @@
         // the user where a dragged item will land when dropped.
         setWillNotDraw(false);
         setClipToPadding(false);
-        mLauncher = Launcher.getLauncher(context);
+        mActivity = ActivityContext.lookupContext(context);
 
-        DeviceProfile grid = mLauncher.getDeviceProfile();
+        DeviceProfile grid = mActivity.getWallpaperDeviceProfile();
 
         mCellWidth = mCellHeight = -1;
         mFixedCellWidth = mFixedCellHeight = -1;
@@ -243,7 +251,7 @@
 
         for (int i = 0; i < mDragOutlineAnims.length; i++) {
             final InterruptibleInOutAnimator anim =
-                new InterruptibleInOutAnimator(this, duration, fromAlphaValue, toAlphaValue);
+                new InterruptibleInOutAnimator(duration, fromAlphaValue, toAlphaValue);
             anim.getAnimator().setInterpolator(mEaseOutInterpolator);
             final int thisIndex = i;
             anim.getAnimator().addUpdateListener(new AnimatorUpdateListener() {
@@ -281,8 +289,6 @@
 
         mShortcutsAndWidgets = new ShortcutAndWidgetContainer(context, mContainerType);
         mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY);
-
-        mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
         addView(mShortcutsAndWidgets);
     }
 
@@ -314,6 +320,24 @@
         }
     }
 
+    public void setRotationMode(RotationMode mode) {
+        if (mRotationMode != mode) {
+            mRotationMode = mode;
+            requestLayout();
+        }
+    }
+
+    @Override
+    public RotationMode getRotationMode() {
+        return mRotationMode;
+    }
+
+    @Override
+    public void setPadding(int left, int top, int right, int bottom) {
+        mRotationMode.mapRect(left, top, right, bottom, mTempRect);
+        super.setPadding(mTempRect.left, mTempRect.top, mTempRect.right, mTempRect.bottom);
+    }
+
     @Override
     public boolean dispatchHoverEvent(MotionEvent event) {
         // Always attempt to dispatch hover events to accessibility first.
@@ -332,24 +356,14 @@
         return false;
     }
 
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        boolean handled = super.onTouchEvent(ev);
-        // Stylus button press on a home screen should not switch between overview mode and
-        // the home screen mode, however, once in overview mode stylus button press should be
-        // enabled to allow rearranging the different home screens. So check what mode
-        // the workspace is in, and only perform stylus button presses while in overview mode.
-        if (mLauncher.isInState(LauncherState.OVERVIEW)
-                && mStylusEventHelper.onMotionEvent(ev)) {
-            return true;
-        }
-        return handled;
-    }
-
     public void enableHardwareLayer(boolean hasLayer) {
         mShortcutsAndWidgets.setLayerType(hasLayer ? LAYER_TYPE_HARDWARE : LAYER_TYPE_NONE, sPaint);
     }
 
+    public boolean isHardwareLayerEnabled() {
+        return mShortcutsAndWidgets.getLayerType() == LAYER_TYPE_HARDWARE;
+    }
+
     public void setCellDimensions(int width, int height) {
         mFixedCellWidth = mCellWidth = width;
         mFixedCellHeight = mCellHeight = height;
@@ -499,7 +513,7 @@
 
     public void setFolderLeaveBehindCell(int x, int y) {
         View child = getChildAt(x, y);
-        mFolderLeaveBehind.setup(mLauncher, null,
+        mFolderLeaveBehind.setup(getContext(), mActivity, null,
                 child.getMeasuredWidth(), child.getPaddingTop());
 
         mFolderLeaveBehind.delegateCellX = x;
@@ -756,6 +770,14 @@
         int heightSize =  MeasureSpec.getSize(heightMeasureSpec);
         int childWidthSize = widthSize - (getPaddingLeft() + getPaddingRight());
         int childHeightSize = heightSize - (getPaddingTop() + getPaddingBottom());
+
+        mShortcutsAndWidgets.setRotation(mRotationMode.surfaceRotation);
+        if (mRotationMode.isTransposed) {
+            int tmp = childWidthSize;
+            childWidthSize = childHeightSize;
+            childHeightSize = tmp;
+        }
+
         if (mFixedCellWidth < 0 || mFixedCellHeight < 0) {
             int cw = DeviceProfile.calculateCellWidth(childWidthSize, mCountX);
             int ch = DeviceProfile.calculateCellHeight(childHeightSize, mCountY);
@@ -798,7 +820,6 @@
         int top = getPaddingTop();
         int bottom = b - t - getPaddingBottom();
 
-        mShortcutsAndWidgets.layout(left, top, right, bottom);
         // Expand the background drawing bounds by the padding baked into the background drawable
         mBackground.getPadding(mTempRect);
         mBackground.setBounds(
@@ -806,6 +827,16 @@
                 top - mTempRect.top - getPaddingTop(),
                 right + mTempRect.right + getPaddingRight(),
                 bottom + mTempRect.bottom + getPaddingBottom());
+
+        if (mRotationMode.isTransposed) {
+            int halfW = mShortcutsAndWidgets.getMeasuredWidth() / 2;
+            int halfH = mShortcutsAndWidgets.getMeasuredHeight() / 2;
+            int cX = (left + right) / 2;
+            int cY = (top + bottom) / 2;
+            mShortcutsAndWidgets.layout(cX - halfW, cY - halfH, cX + halfW, cY + halfH);
+        } else {
+            mShortcutsAndWidgets.layout(left, top, right, bottom);
+        }
     }
 
     /**
@@ -814,7 +845,8 @@
      * width in {@link DeviceProfile#calculateCellWidth(int, int)}.
      */
     public int getUnusedHorizontalSpace() {
-        return getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - (mCountX * mCellWidth);
+        return (mRotationMode.isTransposed ? getMeasuredHeight() : getMeasuredWidth())
+                - getPaddingLeft() - getPaddingRight() - (mCountX * mCellWidth);
     }
 
     public Drawable getScrimBackground() {
@@ -877,7 +909,7 @@
                 return true;
             }
 
-            ValueAnimator va = LauncherAnimUtils.ofFloat(0f, 1f);
+            ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
             va.setDuration(duration);
             mReorderAnimators.put(lp, va);
 
@@ -940,7 +972,7 @@
             if (resize) {
                 cellToRect(cellX, cellY, spanX, spanY, r);
                 if (v instanceof LauncherAppWidgetHostView) {
-                    DeviceProfile profile = mLauncher.getDeviceProfile();
+                    DeviceProfile profile = mActivity.getWallpaperDeviceProfile();
                     Utilities.shrinkRect(r, profile.appWidgetScale.x, profile.appWidgetScale.y);
                 }
             } else {
@@ -1884,6 +1916,19 @@
         }
     }
 
+    private static final Property<ReorderPreviewAnimation, Float> ANIMATION_PROGRESS =
+            new Property<ReorderPreviewAnimation, Float>(float.class, "animationProgress") {
+                @Override
+                public Float get(ReorderPreviewAnimation anim) {
+                    return anim.animationProgress;
+                }
+
+                @Override
+                public void set(ReorderPreviewAnimation anim, Float progress) {
+                    anim.setAnimationProgress(progress);
+                }
+            };
+
     // Class which represents the reorder preview animations. These animations show that an item is
     // in a temporary state, and hint at where the item will return to.
     class ReorderPreviewAnimation {
@@ -1904,7 +1949,8 @@
         public static final int MODE_HINT = 0;
         public static final int MODE_PREVIEW = 1;
 
-        Animator a;
+        float animationProgress = 0;
+        ValueAnimator a;
 
         public ReorderPreviewAnimation(View child, int mode, int cellX0, int cellY0, int cellX1,
                 int cellY1, int spanX, int spanY) {
@@ -1974,33 +2020,19 @@
             if (noMovement) {
                 return;
             }
-            ValueAnimator va = LauncherAnimUtils.ofFloat(0f, 1f);
+            ValueAnimator va = ObjectAnimator.ofFloat(this, ANIMATION_PROGRESS, 0, 1);
             a = va;
 
             // 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.isPowerSaverPreventingAnimation(getContext())) {
+            if (Utilities.areAnimationsEnabled(getContext())) {
                 va.setRepeatMode(ValueAnimator.REVERSE);
                 va.setRepeatCount(ValueAnimator.INFINITE);
             }
 
             va.setDuration(mode == MODE_HINT ? HINT_DURATION : PREVIEW_DURATION);
             va.setStartDelay((int) (Math.random() * 60));
-            va.addUpdateListener(new AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    float r = (Float) animation.getAnimatedValue();
-                    float r1 = (mode == MODE_HINT && repeating) ? 1.0f : r;
-                    float x = r1 * finalDeltaX + (1 - r1) * initDeltaX;
-                    float y = r1 * finalDeltaY + (1 - r1) * initDeltaY;
-                    child.setTranslationX(x);
-                    child.setTranslationY(y);
-                    float s = r * finalScale + (1 - r) * initScale;
-                    child.setScaleX(s);
-                    child.setScaleY(s);
-                }
-            });
             va.addListener(new AnimatorListenerAdapter() {
                 public void onAnimationRepeat(Animator animation) {
                     // We make sure to end only after a full period
@@ -2012,6 +2044,18 @@
             va.start();
         }
 
+        private void setAnimationProgress(float progress) {
+            animationProgress = progress;
+            float r1 = (mode == MODE_HINT && repeating) ? 1.0f : animationProgress;
+            float x = r1 * finalDeltaX + (1 - r1) * initDeltaX;
+            float y = r1 * finalDeltaY + (1 - r1) * initDeltaY;
+            child.setTranslationX(x);
+            child.setTranslationY(y);
+            float s = animationProgress * finalScale + (1 - animationProgress) * initScale;
+            child.setScaleX(s);
+            child.setScaleY(s);
+        }
+
         private void cancel() {
             if (a != null) {
                 a.cancel();
@@ -2024,14 +2068,14 @@
             }
 
             setInitialAnimationValues(true);
-            a = LauncherAnimUtils.ofPropertyValuesHolder(child,
-                    new PropertyListBuilder()
-                            .scale(initScale)
-                            .translationX(initDeltaX)
-                            .translationY(initDeltaY)
-                            .build())
+            a = new PropertyListBuilder()
+                    .scale(initScale)
+                    .translationX(initDeltaX)
+                    .translationY(initDeltaY)
+                    .build(child)
                     .setDuration(REORDER_ANIMATION_DURATION);
-            a.setInterpolator(new android.view.animation.DecelerateInterpolator(1.5f));
+            Launcher.cast(mActivity).getDragController().addFirstFrameAnimationHelper(a);
+            a.setInterpolator(DEACCEL_1_5);
             a.start();
         }
     }
@@ -2046,7 +2090,7 @@
     private void commitTempPlacement() {
         mTmpOccupied.copyTo(mOccupied);
 
-        long screenId = mLauncher.getWorkspace().getIdForScreen(this);
+        int screenId = Launcher.cast(mActivity).getWorkspace().getIdForScreen(this);
         int container = Favorites.CONTAINER_DESKTOP;
 
         if (mContainerType == HOTSEAT) {
@@ -2072,8 +2116,8 @@
                 info.spanY = lp.cellVSpan;
 
                 if (requiresDbUpdate) {
-                    mLauncher.getModelWriter().modifyItemInDatabase(info, container, screenId,
-                            info.cellX, info.cellY, info.spanX, info.spanY);
+                    Launcher.cast(mActivity).getModelWriter().modifyItemInDatabase(info, container,
+                            screenId, info.cellX, info.cellY, info.spanX, info.spanY);
                 }
             }
         }
@@ -2661,38 +2705,6 @@
         public String toString() {
             return "(" + this.cellX + ", " + this.cellY + ")";
         }
-
-        public void setWidth(int width) {
-            this.width = width;
-        }
-
-        public int getWidth() {
-            return width;
-        }
-
-        public void setHeight(int height) {
-            this.height = height;
-        }
-
-        public int getHeight() {
-            return height;
-        }
-
-        public void setX(int x) {
-            this.x = x;
-        }
-
-        public int getX() {
-            return x;
-        }
-
-        public void setY(int y) {
-            this.y = y;
-        }
-
-        public int getY() {
-            return y;
-        }
     }
 
     // This class stores info for two purposes:
@@ -2703,8 +2715,8 @@
     //    the CellLayout that was long clicked
     public static final class CellInfo extends CellAndSpan {
         public final View cell;
-        final long screenId;
-        final long container;
+        final int screenId;
+        final int container;
 
         public CellInfo(View v, ItemInfo info) {
             cellX = info.cellX;
diff --git a/src/com/android/launcher3/CheckLongPressHelper.java b/src/com/android/launcher3/CheckLongPressHelper.java
index dde733c..639c173 100644
--- a/src/com/android/launcher3/CheckLongPressHelper.java
+++ b/src/com/android/launcher3/CheckLongPressHelper.java
@@ -17,17 +17,18 @@
 package com.android.launcher3;
 
 import android.view.View;
+import android.view.ViewConfiguration;
 
 import com.android.launcher3.util.Thunk;
 
 public class CheckLongPressHelper {
 
-    public static final int DEFAULT_LONG_PRESS_TIMEOUT = 300;
+    public static final float DEFAULT_LONG_PRESS_TIMEOUT_FACTOR = 0.75f;
 
     @Thunk View mView;
     @Thunk View.OnLongClickListener mListener;
     @Thunk boolean mHasPerformedLongPress;
-    private int mLongPressTimeout = DEFAULT_LONG_PRESS_TIMEOUT;
+    private float mLongPressTimeoutFactor = DEFAULT_LONG_PRESS_TIMEOUT_FACTOR;
     private CheckForLongPress mPendingCheckForLongPress;
 
     class CheckForLongPress implements Runnable {
@@ -60,8 +61,8 @@
     /**
      * Overrides the default long press timeout.
      */
-    public void setLongPressTimeout(int longPressTimeout) {
-        mLongPressTimeout = longPressTimeout;
+    public void setLongPressTimeoutFactor(float longPressTimeoutFactor) {
+        mLongPressTimeoutFactor = longPressTimeoutFactor;
     }
 
     public void postCheckForLongPress() {
@@ -70,7 +71,8 @@
         if (mPendingCheckForLongPress == null) {
             mPendingCheckForLongPress = new CheckForLongPress();
         }
-        mView.postDelayed(mPendingCheckForLongPress, mLongPressTimeout);
+        mView.postDelayed(mPendingCheckForLongPress,
+                (long) (ViewConfiguration.getLongPressTimeout() * mLongPressTimeoutFactor));
     }
 
     public void cancelLongPress() {
diff --git a/src/com/android/launcher3/DefaultLayoutParser.java b/src/com/android/launcher3/DefaultLayoutParser.java
index 1ec30ba..75297f6 100644
--- a/src/com/android/launcher3/DefaultLayoutParser.java
+++ b/src/com/android/launcher3/DefaultLayoutParser.java
@@ -10,7 +10,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
-import android.content.res.XmlResourceParser;
 import android.os.Bundle;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -76,13 +75,13 @@
     }
 
     @Override
-    protected void parseContainerAndScreen(XmlResourceParser parser, long[] out) {
+    protected void parseContainerAndScreen(XmlPullParser parser, int[] out) {
         out[0] = LauncherSettings.Favorites.CONTAINER_DESKTOP;
         String strContainer = getAttributeValue(parser, ATTR_CONTAINER);
         if (strContainer != null) {
-            out[0] = Long.valueOf(strContainer);
+            out[0] = Integer.parseInt(strContainer);
         }
-        out[1] = Long.parseLong(getAttributeValue(parser, ATTR_SCREEN));
+        out[1] = Integer.parseInt(getAttributeValue(parser, ATTR_SCREEN));
     }
 
     /**
@@ -91,7 +90,7 @@
     public class AppShortcutWithUriParser extends AppShortcutParser {
 
         @Override
-        protected long invalidPackageOrClass(XmlResourceParser parser) {
+        protected int invalidPackageOrClass(XmlPullParser parser) {
             final String uri = getAttributeValue(parser, ATTR_URI);
             if (TextUtils.isEmpty(uri)) {
                 Log.e(TAG, "Skipping invalid <favorite> with no component or uri");
@@ -185,7 +184,7 @@
         }
 
         @Override
-        protected Intent parseIntent(XmlResourceParser parser) {
+        protected Intent parseIntent(XmlPullParser parser) {
             String uri = null;
             try {
                 uri = getAttributeValue(parser, ATTR_URI);
@@ -205,11 +204,11 @@
         private final AppShortcutWithUriParser mChildParser = new AppShortcutWithUriParser();
 
         @Override
-        public long parseAndAdd(XmlResourceParser parser) throws XmlPullParserException,
+        public int parseAndAdd(XmlPullParser parser) throws XmlPullParserException,
                 IOException {
             final int groupDepth = parser.getDepth();
             int type;
-            long addedId = -1;
+            int addedId = -1;
             while ((type = parser.next()) != XmlPullParser.END_TAG ||
                     parser.getDepth() > groupDepth) {
                 if (type != XmlPullParser.START_TAG || addedId > -1) {
@@ -233,7 +232,7 @@
     @Thunk class PartnerFolderParser implements TagParser {
 
         @Override
-        public long parseAndAdd(XmlResourceParser parser) throws XmlPullParserException,
+        public int parseAndAdd(XmlPullParser parser) throws XmlPullParserException,
                 IOException {
             // Folder contents come from an external XML resource
             final Partner partner = Partner.get(mPackageManager);
@@ -242,7 +241,7 @@
                 final int resId = partnerRes.getIdentifier(Partner.RES_FOLDER,
                         "xml", partner.getPackageName());
                 if (resId != 0) {
-                    final XmlResourceParser partnerParser = partnerRes.getXml(resId);
+                    final XmlPullParser partnerParser = partnerRes.getXml(resId);
                     beginDocument(partnerParser, TAG_FOLDER);
 
                     FolderParser folderParser = new FolderParser(getFolderElementsMap(partnerRes));
@@ -259,7 +258,7 @@
     @Thunk class MyFolderParser extends FolderParser {
 
         @Override
-        public long parseAndAdd(XmlResourceParser parser) throws XmlPullParserException,
+        public int parseAndAdd(XmlPullParser parser) throws XmlPullParserException,
                 IOException {
             final int resId = getAttributeResourceValue(parser, ATTR_FOLDER_ITEMS, 0);
             if (resId != 0) {
@@ -277,7 +276,7 @@
     protected class AppWidgetParser extends PendingWidgetParser {
 
         @Override
-        protected long verifyAndInsert(ComponentName cn, Bundle extras) {
+        protected int verifyAndInsert(ComponentName cn, Bundle extras) {
             try {
                 mPackageManager.getReceiverInfo(cn, 0);
             } catch (Exception e) {
@@ -293,7 +292,7 @@
             }
 
             final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
-            long insertedId = -1;
+            int insertedId = -1;
             try {
                 int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
 
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index 64a58fb..3347b2a 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -16,6 +16,9 @@
 
 package com.android.launcher3;
 
+import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.UNDO;
+
 import android.content.Context;
 import android.text.TextUtils;
 import android.util.AttributeSet;
@@ -23,10 +26,11 @@
 
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.dragndrop.DragOptions;
-import com.android.launcher3.folder.Folder;
 import com.android.launcher3.logging.LoggerUtils;
+import com.android.launcher3.model.ModelWriter;
 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 {
 
@@ -61,8 +65,12 @@
      */
     @Override
     public boolean supportsAccessibilityDrop(ItemInfo info, View view) {
-        return (info instanceof ShortcutInfo)
-                || (info instanceof LauncherAppWidgetInfo)
+        if (info instanceof WorkspaceItemInfo) {
+            // Support the action unless the item is in a context menu.
+            return info.screenId >= 0;
+        }
+
+        return (info instanceof LauncherAppWidgetInfo)
                 || (info instanceof FolderInfo);
     }
 
@@ -81,13 +89,18 @@
      */
     private void setTextBasedOnDragSource(ItemInfo item) {
         if (!TextUtils.isEmpty(mText)) {
-            mText = getResources().getString(item.id != ItemInfo.NO_ID
+            mText = getResources().getString(canRemove(item)
                     ? R.string.remove_drop_target_label
                     : android.R.string.cancel);
+            setContentDescription(mText);
             requestLayout();
         }
     }
 
+    private boolean canRemove(ItemInfo item) {
+        return item.id != ItemInfo.NO_ID;
+    }
+
     /**
      * Set mControlType depending on the drag item.
      */
@@ -97,10 +110,26 @@
     }
 
     @Override
+    public void onDrop(DragObject d, DragOptions options) {
+        if (canRemove(d.dragInfo)) {
+            mLauncher.getModelWriter().prepareToUndoDelete();
+        }
+        super.onDrop(d, options);
+    }
+
+    @Override
     public void completeDrop(DragObject d) {
         ItemInfo item = d.dragInfo;
-        if ((d.dragSource instanceof Workspace) || (d.dragSource instanceof Folder)) {
+        if (canRemove(item)) {
+            int itemPage = mLauncher.getWorkspace().getCurrentPage();
             onAccessibilityDrop(null, item);
+            ModelWriter modelWriter = mLauncher.getModelWriter();
+            Runnable onUndoClicked = () -> {
+                modelWriter.abortDelete(itemPage);
+                mLauncher.getUserEventDispatcher().logActionOnControl(TAP, UNDO);
+            };
+            Snackbar.show(mLauncher, R.string.item_removed, R.string.undo,
+                    modelWriter::commitDelete, onUndoClicked);
         }
     }
 
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 9839c12..883e8c6 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -16,8 +16,6 @@
 
 package com.android.launcher3;
 
-import android.appwidget.AppWidgetHostView;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -29,8 +27,9 @@
 import android.view.WindowManager;
 
 import com.android.launcher3.CellLayout.ContainerType;
-import com.android.launcher3.badge.BadgeRenderer;
-import com.android.launcher3.graphics.IconNormalizer;
+import com.android.launcher3.graphics.IconShape;
+import com.android.launcher3.icons.DotRenderer;
+import com.android.launcher3.icons.IconNormalizer;
 
 public class DeviceProfile {
 
@@ -50,6 +49,9 @@
     public final int heightPx;
     public final int availableWidthPx;
     public final int availableHeightPx;
+
+    public final float aspectRatio;
+
     /**
      * 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,9 +69,6 @@
     public final int cellLayoutPaddingLeftRightPx;
     public final int cellLayoutBottomPaddingPx;
     public final int edgeMarginPx;
-    public final Rect defaultWidgetPadding;
-    public final int defaultPageSpacingPx;
-    private final int topWorkspacePadding;
     public float workspaceSpringLoadShrinkFactor;
     public final int workspaceSpringLoadedBottomSpace;
 
@@ -126,10 +125,11 @@
     private final Rect mInsets = new Rect();
     public final Rect workspacePadding = new Rect();
     private final Rect mHotseatPadding = new Rect();
+    // When true, nav bar is on the left side of the screen.
     private boolean mIsSeascape;
 
-    // Icon badges
-    public BadgeRenderer mBadgeRenderer;
+    // Notification dots
+    public DotRenderer mDotRenderer;
 
     public DeviceProfile(Context context, InvariantDeviceProfile inv,
             Point minSize, Point maxSize,
@@ -157,7 +157,7 @@
         isTablet = res.getBoolean(R.bool.is_tablet);
         isLargeTablet = res.getBoolean(R.bool.is_large_tablet);
         isPhone = !isTablet && !isLargeTablet;
-        float aspectRatio = ((float) Math.max(widthPx, heightPx)) / Math.min(widthPx, heightPx);
+        aspectRatio = ((float) Math.max(widthPx, heightPx)) / Math.min(widthPx, heightPx);
         boolean isTallDevice = Float.compare(aspectRatio, TALL_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0;
 
         // Some more constants
@@ -169,26 +169,25 @@
                 : Configuration.ORIENTATION_PORTRAIT);
         res = context.getResources();
 
-
-        ComponentName cn = new ComponentName(context.getPackageName(),
-                this.getClass().getName());
-        defaultWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(context, cn, null);
         edgeMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
         desiredWorkspaceLeftRightMarginPx = isVerticalBarLayout() ? 0 : edgeMarginPx;
+
         int cellLayoutPaddingLeftRightMultiplier = !isVerticalBarLayout() && isTablet
                 ? PORTRAIT_TABLET_LEFT_RIGHT_PADDING_MULTIPLIER : 1;
-        cellLayoutPaddingLeftRightPx = cellLayoutPaddingLeftRightMultiplier *
-                res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_layout_padding);
-        cellLayoutBottomPaddingPx =
-                res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_layout_bottom_padding);
+        int cellLayoutPadding = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_layout_padding);
+        if (isLandscape) {
+            cellLayoutPaddingLeftRightPx = 0;
+            cellLayoutBottomPaddingPx = cellLayoutPadding;
+        } else {
+            cellLayoutPaddingLeftRightPx = cellLayoutPaddingLeftRightMultiplier * cellLayoutPadding;
+            cellLayoutBottomPaddingPx = 0;
+        }
+
         verticalDragHandleSizePx = res.getDimensionPixelSize(
                 R.dimen.vertical_drag_handle_size);
         verticalDragHandleOverlapWorkspace =
                 res.getDimensionPixelSize(R.dimen.vertical_drag_handle_overlap_workspace);
-        defaultPageSpacingPx =
-                res.getDimensionPixelSize(R.dimen.dynamic_grid_workspace_page_spacing);
-        topWorkspacePadding =
-                res.getDimensionPixelSize(R.dimen.dynamic_grid_workspace_top_padding);
+
         iconDrawablePaddingOriginalPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_icon_drawable_padding);
         dropTargetBarSizePx = res.getDimensionPixelSize(R.dimen.dynamic_grid_drop_target_size);
@@ -204,14 +203,12 @@
                 + res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_bottom_padding);
         hotseatBarSidePaddingEndPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_side_padding);
-        // Add a bit of space between nav bar and hotseat in multi-window vertical bar layout.
-        hotseatBarSidePaddingStartPx = isMultiWindowMode && isVerticalBarLayout()
-                ? edgeMarginPx : 0;
-        hotseatBarSizePx = isVerticalBarLayout()
-                ? Utilities.pxFromDp(inv.iconSize, dm) + hotseatBarSidePaddingStartPx
-                        + hotseatBarSidePaddingEndPx
-                : res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_size)
-                        + hotseatBarTopPaddingPx + hotseatBarBottomPaddingPx;
+        // Add a bit of space between nav bar and hotseat in vertical bar layout.
+        hotseatBarSidePaddingStartPx = isVerticalBarLayout() ? verticalDragHandleSizePx : 0;
+        hotseatBarSizePx = ResourceUtils.pxFromDp(inv.iconSize, dm) + (isVerticalBarLayout()
+                ? (hotseatBarSidePaddingStartPx + hotseatBarSidePaddingEndPx)
+                : (res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_extra_vertical_size)
+                        + hotseatBarTopPaddingPx + hotseatBarBottomPaddingPx));
 
         // Calculate all of the remaining variables.
         updateAvailableDimensions(dm, res);
@@ -233,7 +230,8 @@
         updateWorkspacePadding();
 
         // This is done last, after iconSizePx is calculated above.
-        mBadgeRenderer = new BadgeRenderer(iconSizePx);
+        mDotRenderer = new DotRenderer(iconSizePx, IconShape.getShapePath(),
+                IconShape.DEFAULT_PATH_SIZE);
     }
 
     public DeviceProfile copy(Context context) {
@@ -313,7 +311,7 @@
         // Workspace
         final boolean isVerticalLayout = isVerticalBarLayout();
         float invIconSizePx = isVerticalLayout ? inv.landscapeIconSize : inv.iconSize;
-        iconSizePx = (int) (Utilities.pxFromDp(invIconSizePx, dm) * scale);
+        iconSizePx = Math.max(1, (int) (ResourceUtils.pxFromDp(invIconSizePx, dm) * scale));
         iconTextSizePx = (int) (Utilities.pxFromSp(inv.iconTextSize, dm) * scale);
         iconDrawablePaddingPx = (int) (iconDrawablePaddingOriginalPx * scale);
 
@@ -350,7 +348,7 @@
 
         if (!isVerticalLayout) {
             int expectedWorkspaceHeight = availableHeightPx - hotseatBarSizePx
-                    - verticalDragHandleSizePx - topWorkspacePadding;
+                    - verticalDragHandleSizePx - edgeMarginPx;
             float minRequiredHeight = dropTargetBarSizePx + workspaceSpringLoadedBottomSpace;
             workspaceSpringLoadShrinkFactor = Math.min(
                     res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f,
@@ -373,7 +371,7 @@
         updateFolderCellSize(1f, dm, res);
 
         // Don't let the folder get too close to the edges of the screen.
-        int folderMargin = edgeMarginPx;
+        int folderMargin = edgeMarginPx * 2;
         Point totalWorkspacePadding = getTotalWorkspacePadding();
 
         // Check if the icons fit within the available height.
@@ -393,7 +391,7 @@
     }
 
     private void updateFolderCellSize(float scale, DisplayMetrics dm, Resources res) {
-        folderChildIconSizePx = (int) (Utilities.pxFromDp(inv.iconSize, dm) * scale);
+        folderChildIconSizePx = (int) (ResourceUtils.pxFromDp(inv.iconSize, dm) * scale);
         folderChildTextSizePx =
                 (int) (res.getDimensionPixelSize(R.dimen.folder_child_text_size) * scale);
 
@@ -412,6 +410,10 @@
         updateWorkspacePadding();
     }
 
+    /**
+     * The current device insets. This is generally same as the insets being dispatched to
+     * {@link Insettable} elements, but can differ if the element is using a different profile.
+     */
     public Rect getInsets() {
         return mInsets;
     }
@@ -461,15 +463,15 @@
                         ((inv.numColumns - 1) * cellWidthPx)));
                 availablePaddingX = (int) Math.min(availablePaddingX,
                         widthPx * MAX_HORIZONTAL_PADDING_PERCENT);
-                int availablePaddingY = Math.max(0, heightPx - topWorkspacePadding - paddingBottom
+                int availablePaddingY = Math.max(0, heightPx - edgeMarginPx - paddingBottom
                         - (2 * inv.numRows * cellHeightPx) - hotseatBarTopPaddingPx
                         - hotseatBarBottomPaddingPx);
-                padding.set(availablePaddingX / 2, topWorkspacePadding + availablePaddingY / 2,
+                padding.set(availablePaddingX / 2, edgeMarginPx + availablePaddingY / 2,
                         availablePaddingX / 2, paddingBottom + availablePaddingY / 2);
             } else {
                 // Pad the top and bottom of the workspace with search/hotseat bar sizes
                 padding.set(desiredWorkspaceLeftRightMarginPx,
-                        topWorkspacePadding,
+                        edgeMarginPx,
                         desiredWorkspaceLeftRightMarginPx,
                         paddingBottom);
             }
diff --git a/src/com/android/launcher3/DragSource.java b/src/com/android/launcher3/DragSource.java
index 93f865c..d4d7b99 100644
--- a/src/com/android/launcher3/DragSource.java
+++ b/src/com/android/launcher3/DragSource.java
@@ -19,7 +19,7 @@
 import android.view.View;
 
 import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.logging.UserEventDispatcher.LogContainerProvider;
+import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
 
 /**
  * Interface defining an object that can originate a drag.
diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java
index 4d30479..763432d 100644
--- a/src/com/android/launcher3/DropTarget.java
+++ b/src/com/android/launcher3/DropTarget.java
@@ -78,17 +78,17 @@
          */
         public final float[] getVisualCenter(float[] recycle) {
             final float res[] = (recycle == null) ? new float[2] : recycle;
+            Rect dragRegion = dragView.getDragRegion();
 
             // These represent the visual top and left of drag view if a dragRect was provided.
             // If a dragRect was not provided, then they correspond to the actual view left and
             // top, as the dragRect is in that case taken to be the entire dragView.
-            // R.dimen.dragViewOffsetY.
-            int left = x - xOffset;
-            int top = y - yOffset;
+            int left = x - xOffset - dragRegion.left;
+            int top = y - yOffset - dragRegion.top;
 
             // In order to find the visual center, we shift by half the dragRect
-            res[0] = left + dragView.getDragRegion().width() / 2;
-            res[1] = top + dragView.getDragRegion().height() / 2;
+            res[0] = left + dragRegion.width() / 2;
+            res[1] = top + dragRegion.height() / 2;
 
             return res;
         }
diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java
index d025a9b..ca001a3 100644
--- a/src/com/android/launcher3/DropTargetBar.java
+++ b/src/com/android/launcher3/DropTargetBar.java
@@ -104,7 +104,7 @@
                         / (2 * (grid.inv.numColumns + 1)))
                         + grid.edgeMarginPx;
             } else {
-                gap = grid.desiredWorkspaceLeftRightMarginPx - grid.defaultWidgetPadding.right;
+                gap = grid.desiredWorkspaceLeftRightMarginPx - grid.inv.defaultWidgetPadding.right;
             }
             lp.width = grid.availableWidthPx - 2 * gap;
 
@@ -204,7 +204,7 @@
         return visibleCount;
     }
 
-    private void animateToVisibility(boolean isVisible) {
+    public void animateToVisibility(boolean isVisible) {
         if (mVisible != isVisible) {
             mVisible = isVisible;
 
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index 9217ca9..7ab88a0 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -17,6 +17,7 @@
 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.graphics.Bitmap;
@@ -34,7 +35,7 @@
 import android.util.Property;
 import android.util.SparseArray;
 
-import com.android.launcher3.graphics.BitmapInfo;
+import com.android.launcher3.icons.BitmapInfo;
 
 public class FastBitmapDrawable extends Drawable {
 
@@ -102,14 +103,19 @@
     }
 
     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 (mScaleAnimation != null) {
+        if (mScale != 1f) {
             int count = canvas.save();
             Rect bounds = getBounds();
             canvas.scale(mScale, mScale, bounds.exactCenterX(), bounds.exactCenterY());
@@ -150,10 +156,23 @@
         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();
@@ -208,8 +227,15 @@
                 mScaleAnimation.setInterpolator(ACCEL);
                 mScaleAnimation.start();
             } else {
-                mScale = 1f;
-                invalidateSelf();
+                if (isVisible()) {
+                    mScaleAnimation = ObjectAnimator.ofFloat(this, SCALE, 1f);
+                    mScaleAnimation.setDuration(CLICK_FEEDBACK_DURATION);
+                    mScaleAnimation.setInterpolator(DEACCEL);
+                    mScaleAnimation.start();
+                } else {
+                    mScale = 1f;
+                    invalidateSelf();
+                }
             }
             return true;
         }
@@ -228,6 +254,10 @@
         }
     }
 
+    protected boolean isDisabled() {
+        return mIsDisabled;
+    }
+
     /**
      * Sets the saturation of this icon, 0 [full color] -> 1 [desaturated]
      */
@@ -317,21 +347,23 @@
 
     @Override
     public ConstantState getConstantState() {
-        return new MyConstantState(mBitmap, mIconColor);
+        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) {
+        public MyConstantState(Bitmap bitmap, int color, boolean isDisabled) {
             mBitmap = bitmap;
             mIconColor = color;
+            mIsDisabled = isDisabled;
         }
 
         @Override
         public Drawable newDrawable() {
-            return new FastBitmapDrawable(mBitmap, mIconColor);
+            return new FastBitmapDrawable(mBitmap, mIconColor, mIsDisabled);
         }
 
         @Override
diff --git a/src/com/android/launcher3/FirstFrameAnimatorHelper.java b/src/com/android/launcher3/FirstFrameAnimatorHelper.java
index e7ca121..c967a96 100644
--- a/src/com/android/launcher3/FirstFrameAnimatorHelper.java
+++ b/src/com/android/launcher3/FirstFrameAnimatorHelper.java
@@ -13,126 +13,124 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package com.android.launcher3;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
+import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
+
 import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.util.Log;
 import android.view.View;
-import android.view.ViewPropertyAnimator;
-import android.view.ViewTreeObserver;
-import com.android.launcher3.util.Thunk;
-
-import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
+import android.view.View.OnAttachStateChangeListener;
+import android.view.ViewTreeObserver.OnDrawListener;
 
 /*
  *  This is a helper class that listens to updates from the corresponding animation.
  *  For the first two frames, it adjusts the current play time of the animation to
  *  prevent jank at the beginning of the animation
  */
-public class FirstFrameAnimatorHelper extends AnimatorListenerAdapter
-    implements ValueAnimator.AnimatorUpdateListener {
+public class FirstFrameAnimatorHelper implements OnDrawListener, OnAttachStateChangeListener {
+
     private static final String TAG = "FirstFrameAnimatorHlpr";
     private static final boolean DEBUG = false;
     private static final int MAX_DELAY = 1000;
-    private final View mTarget;
-    private long mStartFrame;
-    private long mStartTime = -1;
-    private boolean mHandlingOnAnimationUpdate;
-    private boolean mAdjustedSecondFrameTime;
 
-    private static ViewTreeObserver.OnDrawListener sGlobalDrawListener;
-    @Thunk static long sGlobalFrameCounter;
-    private static boolean sVisible;
+    private View mRootView;
+    private long mGlobalFrameCount;
 
-    public FirstFrameAnimatorHelper(ValueAnimator animator, View target) {
-        mTarget = target;
-        animator.addUpdateListener(this);
-    }
-
-    public FirstFrameAnimatorHelper(ViewPropertyAnimator vpa, View target) {
-        mTarget = target;
-        vpa.setListener(this);
-    }
-
-    // only used for ViewPropertyAnimators
-    public void onAnimationStart(Animator animation) {
-        final ValueAnimator va = (ValueAnimator) animation;
-        va.addUpdateListener(FirstFrameAnimatorHelper.this);
-        onAnimationUpdate(va);
-    }
-
-    public static void setIsVisible(boolean visible) {
-        sVisible = visible;
-    }
-
-    public static void initializeDrawListener(View view) {
-        if (sGlobalDrawListener != null) {
-            view.getViewTreeObserver().removeOnDrawListener(sGlobalDrawListener);
+    public FirstFrameAnimatorHelper(View target) {
+        target.addOnAttachStateChangeListener(this);
+        if (target.isAttachedToWindow()) {
+            onViewAttachedToWindow(target);
         }
-
-        sGlobalDrawListener = () -> sGlobalFrameCounter++;
-        view.getViewTreeObserver().addOnDrawListener(sGlobalDrawListener);
-        sVisible = true;
     }
 
-    public void onAnimationUpdate(final ValueAnimator animation) {
-        final long currentTime = System.currentTimeMillis();
-        if (mStartTime == -1) {
-            mStartFrame = sGlobalFrameCounter;
-            mStartTime = currentTime;
+    public <T extends ValueAnimator> T addTo(T anim) {
+        anim.addUpdateListener(new MyListener());
+        return anim;
+    }
+
+    @Override
+    public void onDraw() {
+        mGlobalFrameCount ++;
+    }
+
+    @Override
+    public void onViewAttachedToWindow(View view) {
+        mRootView = view.getRootView();
+        mRootView.getViewTreeObserver().addOnDrawListener(this);
+    }
+
+    @Override
+    public void onViewDetachedFromWindow(View view) {
+        if (mRootView != null) {
+            mRootView.getViewTreeObserver().removeOnDrawListener(this);
+            mRootView = null;
         }
+    }
 
-        final long currentPlayTime = animation.getCurrentPlayTime();
-        boolean isFinalFrame = Float.compare(1f, animation.getAnimatedFraction()) == 0;
+    private class MyListener implements AnimatorUpdateListener {
 
-        if (!mHandlingOnAnimationUpdate &&
-            sVisible &&
-            // If the current play time exceeds the duration, or the animated fraction is 1,
-            // the animation will get finished, even if we call setCurrentPlayTime -- therefore
-            // don't adjust the animation in that case
-            currentPlayTime < animation.getDuration() && !isFinalFrame) {
-            mHandlingOnAnimationUpdate = true;
-            long frameNum = sGlobalFrameCounter - mStartFrame;
-            // If we haven't drawn our first frame, reset the time to t = 0
-            // (give up after MAX_DELAY ms of waiting though - might happen, for example, if we
-            // are no longer in the foreground and no frames are being rendered ever)
-            if (frameNum == 0 && currentTime < mStartTime + MAX_DELAY && currentPlayTime > 0) {
-                // The first frame on animations doesn't always trigger an invalidate...
-                // force an invalidate here to make sure the animation continues to advance
-                mTarget.getRootView().invalidate();
-                animation.setCurrentPlayTime(0);
-            // For the second frame, if the first frame took more than 16ms,
-            // adjust the start time and pretend it took only 16ms anyway. This
-            // prevents a large jump in the animation due to an expensive first frame
-            } else if (frameNum == 1 && currentTime < mStartTime + MAX_DELAY &&
-                       !mAdjustedSecondFrameTime &&
-                       currentTime > mStartTime + SINGLE_FRAME_MS &&
-                       currentPlayTime > SINGLE_FRAME_MS) {
-                animation.setCurrentPlayTime(SINGLE_FRAME_MS);
-                mAdjustedSecondFrameTime = true;
-            } else {
-                if (frameNum > 1) {
-                    mTarget.post(new Runnable() {
-                            public void run() {
-                                animation.removeUpdateListener(FirstFrameAnimatorHelper.this);
-                            }
-                        });
+        private long mStartFrame;
+        private long mStartTime = -1;
+        private boolean mHandlingOnAnimationUpdate;
+        private boolean mAdjustedSecondFrameTime;
+
+        @Override
+        public void onAnimationUpdate(final ValueAnimator animation) {
+            final long currentTime = System.currentTimeMillis();
+            if (mStartTime == -1) {
+                mStartFrame = mGlobalFrameCount;
+                mStartTime = currentTime;
+            }
+
+            final long currentPlayTime = animation.getCurrentPlayTime();
+            boolean isFinalFrame = Float.compare(1f, animation.getAnimatedFraction()) == 0;
+
+            if (!mHandlingOnAnimationUpdate &&
+                    mRootView != null &&
+                    mRootView.getWindowVisibility() == View.VISIBLE &&
+                    // If the current play time exceeds the duration, or the animated fraction is 1,
+                    // the animation will get finished, even if we call setCurrentPlayTime --
+                    // therefore don't adjust the animation in that case
+                    currentPlayTime < animation.getDuration() && !isFinalFrame) {
+                mHandlingOnAnimationUpdate = true;
+                long frameNum = mGlobalFrameCount - mStartFrame;
+
+                // If we haven't drawn our first frame, reset the time to t = 0
+                // (give up after MAX_DELAY ms of waiting though - might happen, for example, if we
+                // are no longer in the foreground and no frames are being rendered ever)
+                if (frameNum == 0 && currentTime < mStartTime + MAX_DELAY && currentPlayTime > 0) {
+                    // The first frame on animations doesn't always trigger an invalidate...
+                    // force an invalidate here to make sure the animation continues to advance
+                    mRootView.invalidate();
+                    animation.setCurrentPlayTime(0);
+                    // For the second frame, if the first frame took more than 16ms,
+                    // adjust the start time and pretend it took only 16ms anyway. This
+                    // prevents a large jump in the animation due to an expensive first frame
+                } else if (frameNum == 1 && currentTime < mStartTime + MAX_DELAY &&
+                        !mAdjustedSecondFrameTime &&
+                        currentTime > mStartTime + SINGLE_FRAME_MS &&
+                        currentPlayTime > SINGLE_FRAME_MS) {
+                    animation.setCurrentPlayTime(SINGLE_FRAME_MS);
+                    mAdjustedSecondFrameTime = true;
+                } else {
+                    if (frameNum > 1) {
+                        mRootView.post(() -> animation.removeUpdateListener(this));
+                    }
+                    if (DEBUG) print(animation);
                 }
+                mHandlingOnAnimationUpdate = false;
+            } else {
                 if (DEBUG) print(animation);
             }
-            mHandlingOnAnimationUpdate = false;
-        } else {
-            if (DEBUG) print(animation);
         }
-    }
 
-    public void print(ValueAnimator animation) {
-        float flatFraction = animation.getCurrentPlayTime() / (float) animation.getDuration();
-        Log.d(TAG, sGlobalFrameCounter +
-              "(" + (sGlobalFrameCounter - mStartFrame) + ") " + mTarget + " dirty? " +
-              mTarget.isDirty() + " " + flatFraction + " " + this + " " + animation);
+        public void print(ValueAnimator animation) {
+            float flatFraction = animation.getCurrentPlayTime() / (float) animation.getDuration();
+            Log.d(TAG, mGlobalFrameCount +
+                    "(" + (mGlobalFrameCount - mStartFrame) + ") " + mRootView + " dirty? " +
+                    mRootView.isDirty() + " " + flatFraction + " " + this + " " + animation);
+        }
     }
 }
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index 21254ab..b747d62 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -50,7 +50,7 @@
     /**
      * The apps and shortcuts
      */
-    public ArrayList<ShortcutInfo> contents = new ArrayList<ShortcutInfo>();
+    public ArrayList<WorkspaceItemInfo> contents = new ArrayList<WorkspaceItemInfo>();
 
     ArrayList<FolderListener> listeners = new ArrayList<FolderListener>();
 
@@ -64,14 +64,14 @@
      *
      * @param item
      */
-    public void add(ShortcutInfo item, boolean animate) {
+    public void add(WorkspaceItemInfo item, boolean animate) {
         add(item, contents.size(), animate);
     }
 
     /**
      * Add an app or shortcut for a specified rank.
      */
-    public void add(ShortcutInfo item, int rank, boolean animate) {
+    public void add(WorkspaceItemInfo item, int rank, boolean animate) {
         rank = Utilities.boundToRange(rank, 0, contents.size());
         contents.add(rank, item);
         for (int i = 0; i < listeners.size(); i++) {
@@ -85,7 +85,7 @@
      *
      * @param item
      */
-    public void remove(ShortcutInfo item, boolean animate) {
+    public void remove(WorkspaceItemInfo item, boolean animate) {
         contents.remove(item);
         for (int i = 0; i < listeners.size(); i++) {
             listeners.get(i).onRemove(item);
@@ -129,8 +129,8 @@
     }
 
     public interface FolderListener {
-        public void onAdd(ShortcutInfo item, int rank);
-        public void onRemove(ShortcutInfo item);
+        public void onAdd(WorkspaceItemInfo item, int rank);
+        public void onRemove(WorkspaceItemInfo item);
         public void onTitleChanged(CharSequence title);
         public void onItemsChanged(boolean animate);
         public void prepareAutoUpdate();
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 6668f2c..00acdcd 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -16,32 +16,23 @@
 
 package com.android.launcher3;
 
-import static com.android.launcher3.LauncherState.ALL_APPS;
-
 import android.content.Context;
 import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
 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 android.widget.TextView;
 
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.logging.UserEventDispatcher.LogContainerProvider;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
+import com.android.launcher3.graphics.RotationMode;
+import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+import com.android.launcher3.views.Transposable;
 
-public class Hotseat extends FrameLayout implements LogContainerProvider, Insettable {
-
-    private final Launcher mLauncher;
-    private CellLayout mContent;
+public class Hotseat extends CellLayout implements LogContainerProvider, Insettable, Transposable {
 
     @ViewDebug.ExportedProperty(category = "launcher")
     private boolean mHasVerticalHotseat;
@@ -56,16 +47,6 @@
 
     public Hotseat(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
-        mLauncher = Launcher.getLauncher(context);
-    }
-
-    public CellLayout getLayout() {
-        return mContent;
-    }
-
-    /* Get the orientation invariant order of the item in the hotseat for persistence. */
-    int getOrderInHotseat(int x, int y) {
-        return mHasVerticalHotseat ? (mContent.getCountY() - y - 1) : x;
     }
 
     /* Get the orientation specific coordinates given an invariant order in the hotseat. */
@@ -74,84 +55,32 @@
     }
 
     int getCellYFromOrder(int rank) {
-        return mHasVerticalHotseat ? (mContent.getCountY() - (rank + 1)) : 0;
+        return mHasVerticalHotseat ? (getCountY() - (rank + 1)) : 0;
     }
 
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mContent = findViewById(R.id.layout);
-    }
-
-    void resetLayout(boolean hasVerticalHotseat) {
-        mContent.removeAllViewsInLayout();
+    public void resetLayout(boolean hasVerticalHotseat) {
+        removeAllViewsInLayout();
         mHasVerticalHotseat = hasVerticalHotseat;
-        InvariantDeviceProfile idp = mLauncher.getDeviceProfile().inv;
+        InvariantDeviceProfile idp = mActivity.getDeviceProfile().inv;
         if (hasVerticalHotseat) {
-            mContent.setGridSize(1, idp.numHotseatIcons);
+            setGridSize(1, idp.numHotseatIcons);
         } else {
-            mContent.setGridSize(idp.numHotseatIcons, 1);
+            setGridSize(idp.numHotseatIcons, 1);
         }
-
-        if (!FeatureFlags.NO_ALL_APPS_ICON) {
-            // Add the Apps button
-            Context context = getContext();
-            DeviceProfile grid = mLauncher.getDeviceProfile();
-            int allAppsButtonRank = grid.inv.getAllAppsButtonRank();
-
-            LayoutInflater inflater = LayoutInflater.from(context);
-            TextView allAppsButton = (TextView)
-                    inflater.inflate(R.layout.all_apps_button, mContent, false);
-            Drawable d = context.getResources().getDrawable(R.drawable.all_apps_button_icon);
-            d.setBounds(0, 0, grid.iconSizePx, grid.iconSizePx);
-
-            int scaleDownPx = getResources().getDimensionPixelSize(R.dimen.all_apps_button_scale_down);
-            Rect bounds = d.getBounds();
-            d.setBounds(bounds.left, bounds.top + scaleDownPx / 2, bounds.right - scaleDownPx,
-                    bounds.bottom - scaleDownPx / 2);
-            allAppsButton.setCompoundDrawables(null, d, null, null);
-
-            allAppsButton.setContentDescription(context.getString(R.string.all_apps_button_label));
-            if (mLauncher != null) {
-                allAppsButton.setOnClickListener((v) -> {
-                    if (!mLauncher.isInState(ALL_APPS)) {
-                        mLauncher.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
-                                ControlType.ALL_APPS_BUTTON);
-                        mLauncher.getStateManager().goToState(ALL_APPS);
-                    }
-                });
-                allAppsButton.setOnFocusChangeListener(mLauncher.mFocusHandler);
-            }
-
-            // Note: We do this to ensure that the hotseat is always laid out in the orientation of
-            // the hotseat in order regardless of which orientation they were added
-            int x = getCellXFromOrder(allAppsButtonRank);
-            int y = getCellYFromOrder(allAppsButtonRank);
-            CellLayout.LayoutParams lp = new CellLayout.LayoutParams(x, y, 1, 1);
-            lp.canReorder = false;
-            mContent.addViewToCellLayout(allAppsButton, -1, allAppsButton.getId(), lp, true);
-        }
-    }
-
-    @Override
-    public boolean onInterceptTouchEvent(MotionEvent ev) {
-        // We don't want any clicks to go through to the hotseat unless the workspace is in
-        // the normal state or an accessible drag is in progress.
-        return !mLauncher.getWorkspace().workspaceIconsCanBeDragged() &&
-                !mLauncher.getAccessibilityDelegate().isInAccessibleDrag();
     }
 
     @Override
     public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
         target.gridX = info.cellX;
         target.gridY = info.cellY;
-        targetParent.containerType = ContainerType.HOTSEAT;
+        targetParent.containerType = LauncherLogProto.ContainerType.HOTSEAT;
     }
 
     @Override
     public void setInsets(Rect insets) {
         FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
-        DeviceProfile grid = mLauncher.getDeviceProfile();
+        DeviceProfile grid = mActivity.getWallpaperDeviceProfile();
+        insets = grid.getInsets();
 
         if (grid.isVerticalBarLayout()) {
             lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
@@ -168,9 +97,20 @@
             lp.height = grid.hotseatBarSizePx + insets.bottom;
         }
         Rect padding = grid.getHotseatLayoutPadding();
-        getLayout().setPadding(padding.left, padding.top, padding.right, padding.bottom);
+        setPadding(padding.left, padding.top, padding.right, padding.bottom);
 
         setLayoutParams(lp);
         InsettableFrameLayout.dispatchInsets(this, insets);
     }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        // Don't let if follow through to workspace
+        return true;
+    }
+
+    @Override
+    public RotationMode getRotationMode() {
+        return Launcher.getLauncher(getContext()).getRotationMode();
+    }
 }
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
deleted file mode 100644
index c5ca183..0000000
--- a/src/com/android/launcher3/IconCache.java
+++ /dev/null
@@ -1,895 +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 android.content.ComponentName;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.LauncherActivityInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteException;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.drawable.Drawable;
-import android.os.Build.VERSION;
-import android.os.Handler;
-import android.os.Process;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.support.annotation.NonNull;
-import android.support.v4.graphics.ColorUtils;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.launcher3.compat.LauncherAppsCompat;
-import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.graphics.BitmapInfo;
-import com.android.launcher3.graphics.BitmapRenderer;
-import com.android.launcher3.graphics.LauncherIcons;
-import com.android.launcher3.model.PackageItemInfo;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.InstantAppResolver;
-import com.android.launcher3.util.Preconditions;
-import com.android.launcher3.util.Provider;
-import com.android.launcher3.util.SQLiteCacheHelper;
-import com.android.launcher3.util.Thunk;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.Stack;
-
-/**
- * Cache of application icons.  Icons can be made from any thread.
- */
-public class IconCache {
-
-    private static final String TAG = "Launcher.IconCache";
-
-    private static final int INITIAL_ICON_CACHE_CAPACITY = 50;
-
-    // Empty class name is used for storing package default entry.
-    public static final String EMPTY_CLASS_NAME = ".";
-
-    private static final boolean DEBUG = false;
-    private static final boolean DEBUG_IGNORE_CACHE = false;
-
-    private static final int LOW_RES_SCALE_FACTOR = 5;
-
-    @Thunk static final Object ICON_UPDATE_TOKEN = new Object();
-
-    public static class CacheEntry extends BitmapInfo {
-        public CharSequence title = "";
-        public CharSequence contentDescription = "";
-        public boolean isLowResIcon;
-    }
-
-    private final HashMap<UserHandle, BitmapInfo> mDefaultIcons = new HashMap<>();
-    @Thunk final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
-
-    private final Context mContext;
-    private final PackageManager mPackageManager;
-    private final IconProvider mIconProvider;
-    @Thunk final UserManagerCompat mUserManager;
-    private final LauncherAppsCompat mLauncherApps;
-    private final HashMap<ComponentKey, CacheEntry> mCache =
-            new HashMap<>(INITIAL_ICON_CACHE_CAPACITY);
-    private final InstantAppResolver mInstantAppResolver;
-    private final int mIconDpi;
-    @Thunk final IconDB mIconDb;
-
-    @Thunk final Handler mWorkerHandler;
-
-    private final BitmapFactory.Options mLowResOptions;
-    private final BitmapFactory.Options mHighResOptions;
-
-    private int mPendingIconRequestCount = 0;
-
-    public IconCache(Context context, InvariantDeviceProfile inv) {
-        mContext = context;
-        mPackageManager = context.getPackageManager();
-        mUserManager = UserManagerCompat.getInstance(mContext);
-        mLauncherApps = LauncherAppsCompat.getInstance(mContext);
-        mInstantAppResolver = InstantAppResolver.newInstance(mContext);
-        mIconDpi = inv.fillResIconDpi;
-        mIconDb = new IconDB(context, inv.iconBitmapSize);
-
-        mIconProvider = IconProvider.newInstance(context);
-        mWorkerHandler = new Handler(LauncherModel.getWorkerLooper());
-
-        mLowResOptions = new BitmapFactory.Options();
-        // Always prefer RGB_565 config for low res. If the bitmap has transparency, it will
-        // automatically be loaded as ALPHA_8888.
-        mLowResOptions.inPreferredConfig = Bitmap.Config.RGB_565;
-
-        if (BitmapRenderer.USE_HARDWARE_BITMAP) {
-            mHighResOptions = new BitmapFactory.Options();
-            mHighResOptions.inPreferredConfig = Bitmap.Config.HARDWARE;
-        } else {
-            mHighResOptions = null;
-        }
-    }
-
-    private Drawable getFullResDefaultActivityIcon() {
-        return getFullResIcon(Resources.getSystem(), Utilities.ATLEAST_OREO ?
-                android.R.drawable.sym_def_app_icon : android.R.mipmap.sym_def_app_icon);
-    }
-
-    private Drawable getFullResIcon(Resources resources, int iconId) {
-        Drawable d;
-        try {
-            d = resources.getDrawableForDensity(iconId, mIconDpi);
-        } catch (Resources.NotFoundException e) {
-            d = null;
-        }
-
-        return (d != null) ? d : getFullResDefaultActivityIcon();
-    }
-
-    public Drawable getFullResIcon(String packageName, int iconId) {
-        Resources resources;
-        try {
-            resources = mPackageManager.getResourcesForApplication(packageName);
-        } catch (PackageManager.NameNotFoundException e) {
-            resources = null;
-        }
-        if (resources != null) {
-            if (iconId != 0) {
-                return getFullResIcon(resources, iconId);
-            }
-        }
-        return getFullResDefaultActivityIcon();
-    }
-
-    public Drawable getFullResIcon(ActivityInfo info) {
-        Resources resources;
-        try {
-            resources = mPackageManager.getResourcesForApplication(
-                    info.applicationInfo);
-        } catch (PackageManager.NameNotFoundException e) {
-            resources = null;
-        }
-        if (resources != null) {
-            int iconId = info.getIconResource();
-            if (iconId != 0) {
-                return getFullResIcon(resources, iconId);
-            }
-        }
-
-        return getFullResDefaultActivityIcon();
-    }
-
-    public Drawable getFullResIcon(LauncherActivityInfo info) {
-        return getFullResIcon(info, true);
-    }
-
-    public Drawable getFullResIcon(LauncherActivityInfo info, boolean flattenDrawable) {
-        return mIconProvider.getIcon(info, mIconDpi, flattenDrawable);
-    }
-
-    protected BitmapInfo makeDefaultIcon(UserHandle user) {
-        try (LauncherIcons li = LauncherIcons.obtain(mContext)) {
-            return li.createBadgedIconBitmap(
-                    getFullResDefaultActivityIcon(), user, VERSION.SDK_INT);
-        }
-    }
-
-    /**
-     * Remove any records for the supplied ComponentName.
-     */
-    public synchronized void remove(ComponentName componentName, UserHandle user) {
-        mCache.remove(new ComponentKey(componentName, user));
-    }
-
-    /**
-     * Remove any records for the supplied package name from memory.
-     */
-    private void removeFromMemCacheLocked(String packageName, UserHandle user) {
-        HashSet<ComponentKey> forDeletion = new HashSet<>();
-        for (ComponentKey key: mCache.keySet()) {
-            if (key.componentName.getPackageName().equals(packageName)
-                    && key.user.equals(user)) {
-                forDeletion.add(key);
-            }
-        }
-        for (ComponentKey condemned: forDeletion) {
-            mCache.remove(condemned);
-        }
-    }
-
-    /**
-     * Updates the entries related to the given package in memory and persistent DB.
-     */
-    public synchronized void updateIconsForPkg(String packageName, UserHandle user) {
-        removeIconsForPkg(packageName, user);
-        try {
-            PackageInfo info = mPackageManager.getPackageInfo(packageName,
-                    PackageManager.GET_UNINSTALLED_PACKAGES);
-            long userSerial = mUserManager.getSerialNumberForUser(user);
-            for (LauncherActivityInfo app : mLauncherApps.getActivityList(packageName, user)) {
-                addIconToDBAndMemCache(app, info, userSerial, false /*replace existing*/);
-            }
-        } catch (NameNotFoundException e) {
-            Log.d(TAG, "Package not found", e);
-        }
-    }
-
-    /**
-     * Removes the entries related to the given package in memory and persistent DB.
-     */
-    public synchronized void removeIconsForPkg(String packageName, UserHandle user) {
-        removeFromMemCacheLocked(packageName, user);
-        long userSerial = mUserManager.getSerialNumberForUser(user);
-        mIconDb.delete(
-                IconDB.COLUMN_COMPONENT + " LIKE ? AND " + IconDB.COLUMN_USER + " = ?",
-                new String[]{packageName + "/%", Long.toString(userSerial)});
-    }
-
-    public void updateDbIcons(Set<String> ignorePackagesForMainUser) {
-        // Remove all active icon update tasks.
-        mWorkerHandler.removeCallbacksAndMessages(ICON_UPDATE_TOKEN);
-
-        mIconProvider.updateSystemStateString(mContext);
-        for (UserHandle user : mUserManager.getUserProfiles()) {
-            // Query for the set of apps
-            final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user);
-            // Fail if we don't have any apps
-            // TODO: Fix this. Only fail for the current user.
-            if (apps == null || apps.isEmpty()) {
-                return;
-            }
-
-            // Update icon cache. This happens in segments and {@link #onPackageIconsUpdated}
-            // is called by the icon cache when the job is complete.
-            updateDBIcons(user, apps, Process.myUserHandle().equals(user)
-                    ? ignorePackagesForMainUser : Collections.<String>emptySet());
-        }
-    }
-
-    /**
-     * Updates the persistent DB, such that only entries corresponding to {@param apps} remain in
-     * the DB and are updated.
-     * @return The set of packages for which icons have updated.
-     */
-    private void updateDBIcons(UserHandle user, List<LauncherActivityInfo> apps,
-            Set<String> ignorePackages) {
-        long userSerial = mUserManager.getSerialNumberForUser(user);
-        PackageManager pm = mContext.getPackageManager();
-        HashMap<String, PackageInfo> pkgInfoMap = new HashMap<>();
-        for (PackageInfo info : pm.getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES)) {
-            pkgInfoMap.put(info.packageName, info);
-        }
-
-        HashMap<ComponentName, LauncherActivityInfo> componentMap = new HashMap<>();
-        for (LauncherActivityInfo app : apps) {
-            componentMap.put(app.getComponentName(), app);
-        }
-
-        HashSet<Integer> itemsToRemove = new HashSet<>();
-        Stack<LauncherActivityInfo> appsToUpdate = new Stack<>();
-
-        Cursor c = null;
-        try {
-            c = mIconDb.query(
-                    new String[]{IconDB.COLUMN_ROWID, IconDB.COLUMN_COMPONENT,
-                            IconDB.COLUMN_LAST_UPDATED, IconDB.COLUMN_VERSION,
-                            IconDB.COLUMN_SYSTEM_STATE},
-                    IconDB.COLUMN_USER + " = ? ",
-                    new String[]{Long.toString(userSerial)});
-
-            final int indexComponent = c.getColumnIndex(IconDB.COLUMN_COMPONENT);
-            final int indexLastUpdate = c.getColumnIndex(IconDB.COLUMN_LAST_UPDATED);
-            final int indexVersion = c.getColumnIndex(IconDB.COLUMN_VERSION);
-            final int rowIndex = c.getColumnIndex(IconDB.COLUMN_ROWID);
-            final int systemStateIndex = c.getColumnIndex(IconDB.COLUMN_SYSTEM_STATE);
-
-            while (c.moveToNext()) {
-                String cn = c.getString(indexComponent);
-                ComponentName component = ComponentName.unflattenFromString(cn);
-                PackageInfo info = pkgInfoMap.get(component.getPackageName());
-                if (info == null) {
-                    if (!ignorePackages.contains(component.getPackageName())) {
-                        remove(component, user);
-                        itemsToRemove.add(c.getInt(rowIndex));
-                    }
-                    continue;
-                }
-                if ((info.applicationInfo.flags & ApplicationInfo.FLAG_IS_DATA_ONLY) != 0) {
-                    // Application is not present
-                    continue;
-                }
-
-                long updateTime = c.getLong(indexLastUpdate);
-                int version = c.getInt(indexVersion);
-                LauncherActivityInfo app = componentMap.remove(component);
-                if (version == info.versionCode && updateTime == info.lastUpdateTime &&
-                        TextUtils.equals(c.getString(systemStateIndex),
-                                mIconProvider.getIconSystemState(info.packageName))) {
-                    continue;
-                }
-                if (app == null) {
-                    remove(component, user);
-                    itemsToRemove.add(c.getInt(rowIndex));
-                } else {
-                    appsToUpdate.add(app);
-                }
-            }
-        } catch (SQLiteException e) {
-            Log.d(TAG, "Error reading icon cache", e);
-            // Continue updating whatever we have read so far
-        } finally {
-            if (c != null) {
-                c.close();
-            }
-        }
-        if (!itemsToRemove.isEmpty()) {
-            mIconDb.delete(
-                    Utilities.createDbSelectionQuery(IconDB.COLUMN_ROWID, itemsToRemove), null);
-        }
-
-        // Insert remaining apps.
-        if (!componentMap.isEmpty() || !appsToUpdate.isEmpty()) {
-            Stack<LauncherActivityInfo> appsToAdd = new Stack<>();
-            appsToAdd.addAll(componentMap.values());
-            new SerializedIconUpdateTask(userSerial, pkgInfoMap,
-                    appsToAdd, appsToUpdate).scheduleNext();
-        }
-    }
-
-    /**
-     * Adds an entry into the DB and the in-memory cache.
-     * @param replaceExisting if true, it will recreate the bitmap even if it already exists in
-     *                        the memory. This is useful then the previous bitmap was created using
-     *                        old data.
-     */
-    @Thunk synchronized void addIconToDBAndMemCache(LauncherActivityInfo app,
-            PackageInfo info, long userSerial, boolean replaceExisting) {
-        final ComponentKey key = new ComponentKey(app.getComponentName(), app.getUser());
-        CacheEntry entry = null;
-        if (!replaceExisting) {
-            entry = mCache.get(key);
-            // We can't reuse the entry if the high-res icon is not present.
-            if (entry == null || entry.isLowResIcon || entry.icon == null) {
-                entry = null;
-            }
-        }
-        if (entry == null) {
-            entry = new CacheEntry();
-            LauncherIcons li = LauncherIcons.obtain(mContext);
-            li.createBadgedIconBitmap(getFullResIcon(app), app.getUser(),
-                    app.getApplicationInfo().targetSdkVersion).applyTo(entry);
-            li.recycle();
-        }
-        entry.title = app.getLabel();
-        entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, app.getUser());
-        mCache.put(key, entry);
-
-        Bitmap lowResIcon = generateLowResIcon(entry.icon);
-        ContentValues values = newContentValues(entry.icon, lowResIcon, entry.color,
-                entry.title.toString(), app.getApplicationInfo().packageName);
-        addIconToDB(values, app.getComponentName(), info, userSerial);
-    }
-
-    /**
-     * Updates {@param values} to contain versioning information and adds it to the DB.
-     * @param values {@link ContentValues} containing icon & title
-     */
-    private void addIconToDB(ContentValues values, ComponentName key,
-            PackageInfo info, long userSerial) {
-        values.put(IconDB.COLUMN_COMPONENT, key.flattenToString());
-        values.put(IconDB.COLUMN_USER, userSerial);
-        values.put(IconDB.COLUMN_LAST_UPDATED, info.lastUpdateTime);
-        values.put(IconDB.COLUMN_VERSION, info.versionCode);
-        mIconDb.insertOrReplace(values);
-    }
-
-    /**
-     * 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,
-            final ItemInfoWithIcon info) {
-        Preconditions.assertUIThread();
-        if (mPendingIconRequestCount <= 0) {
-            LauncherModel.setWorkerPriority(Process.THREAD_PRIORITY_FOREGROUND);
-        }
-        mPendingIconRequestCount ++;
-
-        IconLoadRequest request = new IconLoadRequest(mWorkerHandler, this::onIconRequestEnd) {
-            @Override
-            public void run() {
-                if (info instanceof AppInfo || info instanceof ShortcutInfo) {
-                    getTitleAndIcon(info, false);
-                } else if (info instanceof PackageItemInfo) {
-                    getTitleAndIconForApp((PackageItemInfo) info, false);
-                }
-                mMainThreadExecutor.execute(() -> {
-                    caller.reapplyItemInfo(info);
-                    onEnd();
-                });
-            }
-        };
-        Utilities.postAsyncCallback(mWorkerHandler, request);
-        return request;
-    }
-
-    private void onIconRequestEnd() {
-        mPendingIconRequestCount --;
-        if (mPendingIconRequestCount <= 0) {
-            LauncherModel.setWorkerPriority(Process.THREAD_PRIORITY_BACKGROUND);
-        }
-    }
-
-    /**
-     * Updates {@param application} only if a valid entry is found.
-     */
-    public synchronized void updateTitleAndIcon(AppInfo application) {
-        CacheEntry entry = cacheLocked(application.componentName,
-                Provider.<LauncherActivityInfo>of(null),
-                application.user, false, application.usingLowResIcon);
-        if (entry.icon != null && !isDefaultIcon(entry.icon, application.user)) {
-            applyCacheEntry(entry, application);
-        }
-    }
-
-    /**
-     * Fill in {@param info} with the icon and label for {@param activityInfo}
-     */
-    public synchronized void getTitleAndIcon(ItemInfoWithIcon info,
-            LauncherActivityInfo activityInfo, boolean useLowResIcon) {
-        // If we already have activity info, no need to use package icon
-        getTitleAndIcon(info, Provider.of(activityInfo), false, useLowResIcon);
-    }
-
-    /**
-     * Fill in {@param info} with the icon and label. If the
-     * corresponding activity is not found, it reverts to the package icon.
-     */
-    public synchronized void getTitleAndIcon(ItemInfoWithIcon info, boolean useLowResIcon) {
-        // null info means not installed, but if we have a component from the intent then
-        // we should still look in the cache for restored app icons.
-        if (info.getTargetComponent() == null) {
-            getDefaultIcon(info.user).applyTo(info);
-            info.title = "";
-            info.contentDescription = "";
-            info.usingLowResIcon = false;
-        } else {
-            getTitleAndIcon(info, new ActivityInfoProvider(info.getIntent(), info.user),
-                    true, useLowResIcon);
-        }
-    }
-
-    /**
-     * Fill in {@param shortcutInfo} with the icon and label for {@param info}
-     */
-    private synchronized void getTitleAndIcon(
-            @NonNull ItemInfoWithIcon infoInOut,
-            @NonNull Provider<LauncherActivityInfo> activityInfoProvider,
-            boolean usePkgIcon, boolean useLowResIcon) {
-        CacheEntry entry = cacheLocked(infoInOut.getTargetComponent(), activityInfoProvider,
-                infoInOut.user, usePkgIcon, useLowResIcon);
-        applyCacheEntry(entry, infoInOut);
-    }
-
-    /**
-     * Fill in {@param infoInOut} with the corresponding icon and label.
-     */
-    public synchronized void getTitleAndIconForApp(
-            PackageItemInfo infoInOut, boolean useLowResIcon) {
-        CacheEntry entry = getEntryForPackageLocked(
-                infoInOut.packageName, infoInOut.user, useLowResIcon);
-        applyCacheEntry(entry, infoInOut);
-    }
-
-    private void applyCacheEntry(CacheEntry entry, ItemInfoWithIcon info) {
-        info.title = Utilities.trim(entry.title);
-        info.contentDescription = entry.contentDescription;
-        info.usingLowResIcon = entry.isLowResIcon;
-        ((entry.icon == null) ? getDefaultIcon(info.user) : entry).applyTo(info);
-    }
-
-    public synchronized BitmapInfo getDefaultIcon(UserHandle user) {
-        if (!mDefaultIcons.containsKey(user)) {
-            mDefaultIcons.put(user, makeDefaultIcon(user));
-        }
-        return mDefaultIcons.get(user);
-    }
-
-    public boolean isDefaultIcon(Bitmap icon, UserHandle user) {
-        return getDefaultIcon(user).icon == icon;
-    }
-
-    /**
-     * Retrieves the entry from the cache. If the entry is not present, it creates a new entry.
-     * This method is not thread safe, it must be called from a synchronized method.
-     */
-    protected CacheEntry cacheLocked(
-            @NonNull ComponentName componentName,
-            @NonNull Provider<LauncherActivityInfo> infoProvider,
-            UserHandle user, boolean usePackageIcon, boolean useLowResIcon) {
-        Preconditions.assertWorkerThread();
-        ComponentKey cacheKey = new ComponentKey(componentName, user);
-        CacheEntry entry = mCache.get(cacheKey);
-        if (entry == null || (entry.isLowResIcon && !useLowResIcon)) {
-            entry = new CacheEntry();
-            mCache.put(cacheKey, entry);
-
-            // Check the DB first.
-            LauncherActivityInfo info = null;
-            boolean providerFetchedOnce = false;
-
-            if (!getEntryFromDB(cacheKey, entry, useLowResIcon) || DEBUG_IGNORE_CACHE) {
-                info = infoProvider.get();
-                providerFetchedOnce = true;
-
-                if (info != null) {
-                    LauncherIcons li = LauncherIcons.obtain(mContext);
-                    li.createBadgedIconBitmap(getFullResIcon(info), info.getUser(),
-                            info.getApplicationInfo().targetSdkVersion).applyTo(entry);
-                    li.recycle();
-                } else {
-                    if (usePackageIcon) {
-                        CacheEntry packageEntry = getEntryForPackageLocked(
-                                componentName.getPackageName(), user, false);
-                        if (packageEntry != null) {
-                            if (DEBUG) Log.d(TAG, "using package default icon for " +
-                                    componentName.toShortString());
-                            packageEntry.applyTo(entry);
-                            entry.title = packageEntry.title;
-                            entry.contentDescription = packageEntry.contentDescription;
-                        }
-                    }
-                    if (entry.icon == null) {
-                        if (DEBUG) Log.d(TAG, "using default icon for " +
-                                componentName.toShortString());
-                        getDefaultIcon(user).applyTo(entry);
-                    }
-                }
-            }
-
-            if (TextUtils.isEmpty(entry.title)) {
-                if (info == null && !providerFetchedOnce) {
-                    info = infoProvider.get();
-                    providerFetchedOnce = true;
-                }
-                if (info != null) {
-                    entry.title = info.getLabel();
-                    entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
-                }
-            }
-        }
-        return entry;
-    }
-
-    public synchronized void clear() {
-        Preconditions.assertWorkerThread();
-        mIconDb.clear();
-    }
-
-    /**
-     * Adds a default package entry in the cache. This entry is not persisted and will be removed
-     * when the cache is flushed.
-     */
-    public synchronized void cachePackageInstallInfo(String packageName, UserHandle user,
-            Bitmap icon, CharSequence title) {
-        removeFromMemCacheLocked(packageName, user);
-
-        ComponentKey cacheKey = getPackageKey(packageName, user);
-        CacheEntry entry = mCache.get(cacheKey);
-
-        // For icon caching, do not go through DB. Just update the in-memory entry.
-        if (entry == null) {
-            entry = new CacheEntry();
-        }
-        if (!TextUtils.isEmpty(title)) {
-            entry.title = title;
-        }
-        if (icon != null) {
-            LauncherIcons li = LauncherIcons.obtain(mContext);
-            li.createIconBitmap(icon).applyTo(entry);
-            li.recycle();
-        }
-        if (!TextUtils.isEmpty(title) && entry.icon != null) {
-            mCache.put(cacheKey, entry);
-        }
-    }
-
-    private static ComponentKey getPackageKey(String packageName, UserHandle user) {
-        ComponentName cn = new ComponentName(packageName, packageName + EMPTY_CLASS_NAME);
-        return new ComponentKey(cn, user);
-    }
-
-    /**
-     * Gets an entry for the package, which can be used as a fallback entry for various components.
-     * This method is not thread safe, it must be called from a synchronized method.
-     */
-    private CacheEntry getEntryForPackageLocked(String packageName, UserHandle user,
-            boolean useLowResIcon) {
-        Preconditions.assertWorkerThread();
-        ComponentKey cacheKey = getPackageKey(packageName, user);
-        CacheEntry entry = mCache.get(cacheKey);
-
-        if (entry == null || (entry.isLowResIcon && !useLowResIcon)) {
-            entry = new CacheEntry();
-            boolean entryUpdated = true;
-
-            // Check the DB first.
-            if (!getEntryFromDB(cacheKey, entry, useLowResIcon)) {
-                try {
-                    int flags = Process.myUserHandle().equals(user) ? 0 :
-                        PackageManager.GET_UNINSTALLED_PACKAGES;
-                    PackageInfo info = mPackageManager.getPackageInfo(packageName, flags);
-                    ApplicationInfo appInfo = info.applicationInfo;
-                    if (appInfo == null) {
-                        throw new NameNotFoundException("ApplicationInfo is null");
-                    }
-
-                    LauncherIcons li = LauncherIcons.obtain(mContext);
-                    // Load the full res icon for the application, but if useLowResIcon is set, then
-                    // only keep the low resolution icon instead of the larger full-sized icon
-                    BitmapInfo iconInfo = li.createBadgedIconBitmap(
-                            appInfo.loadIcon(mPackageManager), user, appInfo.targetSdkVersion,
-                            mInstantAppResolver.isInstantApp(appInfo));
-                    li.recycle();
-
-                    Bitmap lowResIcon =  generateLowResIcon(iconInfo.icon);
-                    entry.title = appInfo.loadLabel(mPackageManager);
-                    entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
-                    entry.icon = useLowResIcon ? lowResIcon : iconInfo.icon;
-                    entry.color = iconInfo.color;
-                    entry.isLowResIcon = useLowResIcon;
-
-                    // Add the icon in the DB here, since these do not get written during
-                    // package updates.
-                    ContentValues values = newContentValues(iconInfo.icon, lowResIcon, entry.color,
-                            entry.title.toString(), packageName);
-                    addIconToDB(values, cacheKey.componentName, info,
-                            mUserManager.getSerialNumberForUser(user));
-
-                } catch (NameNotFoundException e) {
-                    if (DEBUG) Log.d(TAG, "Application not installed " + packageName);
-                    entryUpdated = false;
-                }
-            }
-
-            // Only add a filled-out entry to the cache
-            if (entryUpdated) {
-                mCache.put(cacheKey, entry);
-            }
-        }
-        return entry;
-    }
-
-    private boolean getEntryFromDB(ComponentKey cacheKey, CacheEntry entry, boolean lowRes) {
-        Cursor c = null;
-        try {
-            c = mIconDb.query(
-                new String[]{lowRes ? IconDB.COLUMN_ICON_LOW_RES : IconDB.COLUMN_ICON,
-                        IconDB.COLUMN_ICON_COLOR, IconDB.COLUMN_LABEL},
-                IconDB.COLUMN_COMPONENT + " = ? AND " + IconDB.COLUMN_USER + " = ?",
-                new String[]{cacheKey.componentName.flattenToString(),
-                        Long.toString(mUserManager.getSerialNumberForUser(cacheKey.user))});
-            if (c.moveToNext()) {
-                entry.icon = loadIconNoResize(c, 0, lowRes ? mLowResOptions : mHighResOptions);
-                // Set the alpha to be 255, so that we never have a wrong color
-                entry.color = ColorUtils.setAlphaComponent(c.getInt(1), 255);
-                entry.isLowResIcon = lowRes;
-                entry.title = c.getString(2);
-                if (entry.title == null) {
-                    entry.title = "";
-                    entry.contentDescription = "";
-                } else {
-                    entry.contentDescription = mUserManager.getBadgedLabelForUser(
-                            entry.title, cacheKey.user);
-                }
-                return true;
-            }
-        } catch (SQLiteException e) {
-            Log.d(TAG, "Error reading icon cache", e);
-        } finally {
-            if (c != null) {
-                c.close();
-            }
-        }
-        return false;
-    }
-
-    public static abstract class IconLoadRequest implements Runnable {
-        private final Handler mHandler;
-        private final Runnable mEndRunnable;
-
-        private boolean mEnded = false;
-
-        IconLoadRequest(Handler handler, Runnable endRunnable) {
-            mHandler = handler;
-            mEndRunnable = endRunnable;
-        }
-
-        public void cancel() {
-            mHandler.removeCallbacks(this);
-            onEnd();
-        }
-
-        public void onEnd() {
-            if (!mEnded) {
-                mEnded = true;
-                mEndRunnable.run();
-            }
-        }
-    }
-
-    /**
-     * A runnable that updates invalid icons and adds missing icons in the DB for the provided
-     * LauncherActivityInfo list. Items are updated/added one at a time, so that the
-     * worker thread doesn't get blocked.
-     */
-    @Thunk class SerializedIconUpdateTask implements Runnable {
-        private final long mUserSerial;
-        private final HashMap<String, PackageInfo> mPkgInfoMap;
-        private final Stack<LauncherActivityInfo> mAppsToAdd;
-        private final Stack<LauncherActivityInfo> mAppsToUpdate;
-        private final HashSet<String> mUpdatedPackages = new HashSet<>();
-
-        @Thunk SerializedIconUpdateTask(long userSerial, HashMap<String, PackageInfo> pkgInfoMap,
-                Stack<LauncherActivityInfo> appsToAdd,
-                Stack<LauncherActivityInfo> appsToUpdate) {
-            mUserSerial = userSerial;
-            mPkgInfoMap = pkgInfoMap;
-            mAppsToAdd = appsToAdd;
-            mAppsToUpdate = appsToUpdate;
-        }
-
-        @Override
-        public void run() {
-            if (!mAppsToUpdate.isEmpty()) {
-                LauncherActivityInfo app = mAppsToUpdate.pop();
-                String pkg = app.getComponentName().getPackageName();
-                PackageInfo info = mPkgInfoMap.get(pkg);
-                addIconToDBAndMemCache(app, info, mUserSerial, true /*replace existing*/);
-                mUpdatedPackages.add(pkg);
-
-                if (mAppsToUpdate.isEmpty() && !mUpdatedPackages.isEmpty()) {
-                    // No more app to update. Notify model.
-                    LauncherAppState.getInstance(mContext).getModel().onPackageIconsUpdated(
-                            mUpdatedPackages, mUserManager.getUserForSerialNumber(mUserSerial));
-                }
-
-                // Let it run one more time.
-                scheduleNext();
-            } else if (!mAppsToAdd.isEmpty()) {
-                LauncherActivityInfo app = mAppsToAdd.pop();
-                PackageInfo info = mPkgInfoMap.get(app.getComponentName().getPackageName());
-                // We do not check the mPkgInfoMap when generating the mAppsToAdd. Although every
-                // app should have package info, this is not guaranteed by the api
-                if (info != null) {
-                    addIconToDBAndMemCache(app, info, mUserSerial, false /*replace existing*/);
-                }
-
-                if (!mAppsToAdd.isEmpty()) {
-                    scheduleNext();
-                }
-            }
-        }
-
-        public void scheduleNext() {
-            mWorkerHandler.postAtTime(this, ICON_UPDATE_TOKEN, SystemClock.uptimeMillis() + 1);
-        }
-    }
-
-    private static final class IconDB extends SQLiteCacheHelper {
-        private final static int RELEASE_VERSION = 24;
-
-        private final static String TABLE_NAME = "icons";
-        private final static String COLUMN_ROWID = "rowid";
-        private final static String COLUMN_COMPONENT = "componentName";
-        private final static String COLUMN_USER = "profileId";
-        private final static String COLUMN_LAST_UPDATED = "lastUpdated";
-        private final static String COLUMN_VERSION = "version";
-        private final static String COLUMN_ICON = "icon";
-        private final static String COLUMN_ICON_LOW_RES = "icon_low_res";
-        private final static String COLUMN_ICON_COLOR = "icon_color";
-        private final static String COLUMN_LABEL = "label";
-        private final static String COLUMN_SYSTEM_STATE = "system_state";
-
-        public IconDB(Context context, int iconPixelSize) {
-            super(context, LauncherFiles.APP_ICONS_DB,
-                    (RELEASE_VERSION << 16) + iconPixelSize,
-                    TABLE_NAME);
-        }
-
-        @Override
-        protected void onCreateTable(SQLiteDatabase db) {
-            db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" +
-                    COLUMN_COMPONENT + " TEXT NOT NULL, " +
-                    COLUMN_USER + " INTEGER NOT NULL, " +
-                    COLUMN_LAST_UPDATED + " INTEGER NOT NULL DEFAULT 0, " +
-                    COLUMN_VERSION + " INTEGER NOT NULL DEFAULT 0, " +
-                    COLUMN_ICON + " BLOB, " +
-                    COLUMN_ICON_LOW_RES + " BLOB, " +
-                    COLUMN_ICON_COLOR + " INTEGER NOT NULL DEFAULT 0, " +
-                    COLUMN_LABEL + " TEXT, " +
-                    COLUMN_SYSTEM_STATE + " TEXT, " +
-                    "PRIMARY KEY (" + COLUMN_COMPONENT + ", " + COLUMN_USER + ") " +
-                    ");");
-        }
-    }
-
-    private ContentValues newContentValues(Bitmap icon, Bitmap lowResIcon, int iconColor,
-            String label, String packageName) {
-        ContentValues values = new ContentValues();
-        values.put(IconDB.COLUMN_ICON, Utilities.flattenBitmap(icon));
-        values.put(IconDB.COLUMN_ICON_LOW_RES, Utilities.flattenBitmap(lowResIcon));
-        values.put(IconDB.COLUMN_ICON_COLOR, iconColor);
-
-        values.put(IconDB.COLUMN_LABEL, label);
-        values.put(IconDB.COLUMN_SYSTEM_STATE, mIconProvider.getIconSystemState(packageName));
-
-        return values;
-    }
-
-    /**
-     * Generates a new low-res icon given a high-res icon.
-     */
-    private Bitmap generateLowResIcon(Bitmap icon) {
-        return Bitmap.createScaledBitmap(icon,
-                icon.getWidth() / LOW_RES_SCALE_FACTOR,
-                icon.getHeight() / LOW_RES_SCALE_FACTOR, true);
-    }
-
-    private static Bitmap loadIconNoResize(Cursor c, int iconIndex, BitmapFactory.Options options) {
-        byte[] data = c.getBlob(iconIndex);
-        try {
-            return BitmapFactory.decodeByteArray(data, 0, data.length, options);
-        } catch (Exception e) {
-            return null;
-        }
-    }
-
-    private class ActivityInfoProvider extends Provider<LauncherActivityInfo> {
-
-        private final Intent mIntent;
-        private final UserHandle mUser;
-
-        public ActivityInfoProvider(Intent intent, UserHandle user) {
-            mIntent = intent;
-            mUser = user;
-        }
-
-        @Override
-        public LauncherActivityInfo get() {
-            return mLauncherApps.resolveActivity(mIntent, mUser);
-        }
-    }
-
-    /**
-     * Interface for receiving itemInfo with high-res icon.
-     */
-    public interface ItemInfoUpdateReceiver {
-
-        void reapplyItemInfo(ItemInfoWithIcon info);
-    }
-}
diff --git a/src/com/android/launcher3/IconProvider.java b/src/com/android/launcher3/IconProvider.java
index b469a8f..e1ef954 100644
--- a/src/com/android/launcher3/IconProvider.java
+++ b/src/com/android/launcher3/IconProvider.java
@@ -3,36 +3,19 @@
 import android.content.Context;
 import android.content.pm.LauncherActivityInfo;
 import android.graphics.drawable.Drawable;
-import android.os.Build;
 
-import java.util.Locale;
+import com.android.launcher3.util.ResourceBasedOverride;
 
-public class IconProvider {
-
-    protected String mSystemState;
+public class IconProvider implements ResourceBasedOverride {
 
     public static IconProvider newInstance(Context context) {
-        IconProvider provider = Utilities.getOverrideObject(
-                IconProvider.class, context, R.string.icon_provider_class);
-        provider.updateSystemStateString(context);
-        return provider;
+        return Overrides.getObject(IconProvider.class, context, R.string.icon_provider_class);
     }
 
     public IconProvider() { }
 
-    public void updateSystemStateString(Context context) {
-        final String locale;
-        if (Utilities.ATLEAST_NOUGAT) {
-            locale = context.getResources().getConfiguration().getLocales().toLanguageTags();
-        } else {
-            locale = Locale.getDefault().toString();
-        }
-
-        mSystemState = locale + "," + Build.VERSION.SDK_INT;
-    }
-
-    public String getIconSystemState(String packageName) {
-        return mSystemState;
+    public String getSystemStateForPackage(String systemState, String packageName) {
+        return systemState;
     }
 
     /**
diff --git a/src/com/android/launcher3/InsettableFrameLayout.java b/src/com/android/launcher3/InsettableFrameLayout.java
index 1db1fc0..faa18b8 100644
--- a/src/com/android/launcher3/InsettableFrameLayout.java
+++ b/src/com/android/launcher3/InsettableFrameLayout.java
@@ -68,7 +68,7 @@
     }
 
     public static class LayoutParams extends FrameLayout.LayoutParams {
-        boolean ignoreInsets = false;
+        public boolean ignoreInsets = false;
 
         public LayoutParams(Context c, AttributeSet attrs) {
             super(c, attrs);
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index fe8a841..e9b932a 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -25,6 +25,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ShortcutInfo;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.os.Handler;
@@ -40,14 +41,13 @@
 
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.graphics.BitmapInfo;
-import com.android.launcher3.graphics.LauncherIcons;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.GraphicsUtils;
+import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.Preconditions;
-import com.android.launcher3.util.Provider;
 import com.android.launcher3.util.Thunk;
 
 import org.json.JSONException;
@@ -57,6 +57,7 @@
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -172,7 +173,7 @@
             Log.d(TAG, "APPS_PENDING_INSTALL: " + strings
                     + ", removing packages: " + packageNames);
         }
-        if (Utilities.isEmpty(strings)) {
+        if (strings == null || ((Collection) strings).isEmpty()) {
             return;
         }
         Set<String> newStrings = new HashSet<>(strings);
@@ -243,16 +244,16 @@
         return convertToLauncherActivityIfPossible(info);
     }
 
-    public static ShortcutInfo fromShortcutIntent(Context context, Intent data) {
+    public static WorkspaceItemInfo fromShortcutIntent(Context context, Intent data) {
         PendingInstallShortcutInfo info = createPendingInfo(context, data);
-        return info == null ? null : (ShortcutInfo) info.getItemInfo().first;
+        return info == null ? null : (WorkspaceItemInfo) info.getItemInfo().first;
     }
 
-    public static ShortcutInfo fromActivityInfo(LauncherActivityInfo info, Context context) {
-        return (ShortcutInfo) (new PendingInstallShortcutInfo(info, context).getItemInfo().first);
+    public static WorkspaceItemInfo fromActivityInfo(LauncherActivityInfo info, Context context) {
+        return (WorkspaceItemInfo) (new PendingInstallShortcutInfo(info, context).getItemInfo().first);
     }
 
-    public static void queueShortcut(ShortcutInfoCompat info, Context context) {
+    public static void queueShortcut(ShortcutInfo info, Context context) {
         queuePendingShortcutInfo(new PendingInstallShortcutInfo(info, context), context);
     }
 
@@ -268,7 +269,7 @@
         HashSet<ShortcutKey> result = new HashSet<>();
 
         Set<String> strings = Utilities.getPrefs(context).getStringSet(APPS_PENDING_INSTALL, null);
-        if (Utilities.isEmpty(strings)) {
+        if (strings == null || ((Collection) strings).isEmpty()) {
             return result;
         }
 
@@ -326,7 +327,7 @@
     private static class PendingInstallShortcutInfo {
 
         final LauncherActivityInfo activityInfo;
-        final ShortcutInfoCompat shortcutInfo;
+        final ShortcutInfo shortcutInfo;
         final AppWidgetProviderInfo providerInfo;
 
         final Intent data;
@@ -371,7 +372,7 @@
         /**
          * Initializes a PendingInstallShortcutInfo to represent a launcher target.
          */
-        public PendingInstallShortcutInfo(ShortcutInfoCompat info, Context context) {
+        public PendingInstallShortcutInfo(ShortcutInfo info, Context context) {
             activityInfo = null;
             shortcutInfo = info;
             providerInfo = null;
@@ -380,7 +381,7 @@
             mContext = context;
             user = info.getUserHandle();
 
-            launchIntent = info.makeIntent();
+            launchIntent = ShortcutKey.makeIntent(info);
             label = info.getShortLabel().toString();
         }
 
@@ -458,7 +459,7 @@
                     .key(LAUNCH_INTENT_KEY).value(launchIntent.toUri(0))
                     .key(NAME_KEY).value(name);
                 if (icon != null) {
-                    byte[] iconByteArray = Utilities.flattenBitmap(icon);
+                    byte[] iconByteArray = GraphicsUtils.flattenBitmap(icon);
                     json = json.key(ICON_KEY).value(
                             Base64.encodeToString(
                                     iconByteArray, 0, iconByteArray.length, Base64.DEFAULT));
@@ -481,25 +482,22 @@
                 final LauncherAppState app = LauncherAppState.getInstance(mContext);
                 // Set default values until proper values is loaded.
                 appInfo.title = "";
-                app.getIconCache().getDefaultIcon(user).applyTo(appInfo);
-                final ShortcutInfo si = appInfo.makeShortcut();
+                appInfo.applyFrom(app.getIconCache().getDefaultIcon(user));
+                final WorkspaceItemInfo si = appInfo.makeWorkspaceItem();
                 if (Looper.myLooper() == LauncherModel.getWorkerLooper()) {
                     app.getIconCache().getTitleAndIcon(si, activityInfo, false /* useLowResIcon */);
                 } else {
-                    app.getModel().updateAndBindShortcutInfo(new Provider<ShortcutInfo>() {
-                        @Override
-                        public ShortcutInfo get() {
-                            app.getIconCache().getTitleAndIcon(
-                                    si, activityInfo, false /* useLowResIcon */);
-                            return si;
-                        }
+                    app.getModel().updateAndBindWorkspaceItem(() -> {
+                        app.getIconCache().getTitleAndIcon(
+                                si, activityInfo, false /* useLowResIcon */);
+                        return si;
                     });
                 }
                 return Pair.create((ItemInfo) si, (Object) activityInfo);
             } else if (shortcutInfo != null) {
-                ShortcutInfo si = new ShortcutInfo(shortcutInfo, mContext);
+                WorkspaceItemInfo si = new WorkspaceItemInfo(shortcutInfo, mContext);
                 LauncherIcons li = LauncherIcons.obtain(mContext);
-                li.createShortcutIcon(shortcutInfo).applyTo(si);
+                si.applyFrom(li.createShortcutIcon(shortcutInfo));
                 li.recycle();
                 return Pair.create((ItemInfo) si, (Object) shortcutInfo);
             } else if (providerInfo != null) {
@@ -515,7 +513,7 @@
                 widgetInfo.spanY = Math.min(info.spanY, idp.numRows);
                 return Pair.create((ItemInfo) widgetInfo, (Object) providerInfo);
             } else {
-                ShortcutInfo si = createShortcutInfo(data, LauncherAppState.getInstance(mContext));
+                WorkspaceItemInfo si = createWorkspaceItemInfo(data, LauncherAppState.getInstance(mContext));
                 return Pair.create((ItemInfo) si, null);
             }
         }
@@ -539,10 +537,10 @@
                 return info == null ? null : new PendingInstallShortcutInfo(info, context);
             } else if (decoder.optBoolean(DEEPSHORTCUT_TYPE_KEY)) {
                 DeepShortcutManager sm = DeepShortcutManager.getInstance(context);
-                List<ShortcutInfoCompat> si = sm.queryForFullDetails(
+                List<ShortcutInfo> si = sm.queryForFullDetails(
                         decoder.launcherIntent.getPackage(),
                         Arrays.asList(decoder.launcherIntent.getStringExtra(
-                                ShortcutInfoCompat.EXTRA_SHORTCUT_ID)),
+                                ShortcutKey.EXTRA_SHORTCUT_ID)),
                         decoder.user);
                 if (si.isEmpty()) {
                     return null;
@@ -627,18 +625,18 @@
         return new PendingInstallShortcutInfo(info, original.mContext);
     }
 
-    private static ShortcutInfo createShortcutInfo(Intent data, LauncherAppState app) {
+    private static WorkspaceItemInfo createWorkspaceItemInfo(Intent data, LauncherAppState app) {
         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, we can't construct a valid ShortcutInfo, so we return null
-            Log.e(TAG, "Can't construct ShorcutInfo with null intent");
+            // 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 ShortcutInfo info = new ShortcutInfo();
+        final WorkspaceItemInfo info = new WorkspaceItemInfo();
 
         // Only support intents for current user for now. Intents sent from other
         // users wouldn't get here without intent forwarding anyway.
@@ -660,11 +658,11 @@
         if (iconInfo == null) {
             iconInfo = app.getIconCache().getDefaultIcon(info.user);
         }
-        iconInfo.applyTo(info);
+        info.applyFrom(iconInfo);
 
         info.title = Utilities.trim(name);
-        info.contentDescription = UserManagerCompat.getInstance(app.getContext())
-                .getBadgedLabelForUser(info.title, info.user);
+        info.contentDescription = app.getContext().getPackageManager()
+                .getUserBadgedLabel(info.title, info.user);
         info.intent = intent;
         return info;
     }
diff --git a/src/com/android/launcher3/InterruptibleInOutAnimator.java b/src/com/android/launcher3/InterruptibleInOutAnimator.java
index 8501e24..f4395ca 100644
--- a/src/com/android/launcher3/InterruptibleInOutAnimator.java
+++ b/src/com/android/launcher3/InterruptibleInOutAnimator.java
@@ -18,7 +18,9 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
+import android.util.Property;
 import android.view.View;
 
 import com.android.launcher3.util.Thunk;
@@ -31,11 +33,27 @@
  * interpolator in the same direction.
  */
 public class InterruptibleInOutAnimator {
+
+    private static final Property<InterruptibleInOutAnimator, Float> VALUE =
+            new Property<InterruptibleInOutAnimator, Float>(Float.TYPE, "value") {
+                @Override
+                public Float get(InterruptibleInOutAnimator anim) {
+                    return anim.mValue;
+                }
+
+                @Override
+                public void set(InterruptibleInOutAnimator anim, Float value) {
+                    anim.mValue = value;
+                }
+            };
+
     private long mOriginalDuration;
     private float mOriginalFromValue;
     private float mOriginalToValue;
     private ValueAnimator mAnimator;
 
+    private float mValue;
+
     private boolean mFirstRun = true;
 
     private Object mTag = null;
@@ -47,8 +65,8 @@
     // TODO: This isn't really necessary, but is here to help diagnose a bug in the drag viz
     @Thunk int mDirection = STOPPED;
 
-    public InterruptibleInOutAnimator(View view, long duration, float fromValue, float toValue) {
-        mAnimator = LauncherAnimUtils.ofFloat(fromValue, toValue).setDuration(duration);
+    public InterruptibleInOutAnimator(long duration, float fromValue, float toValue) {
+        mAnimator = ObjectAnimator.ofFloat(this, VALUE, fromValue, toValue).setDuration(duration);
         mOriginalDuration = duration;
         mOriginalFromValue = fromValue;
         mOriginalToValue = toValue;
@@ -64,8 +82,7 @@
     private void animate(int direction) {
         final long currentPlayTime = mAnimator.getCurrentPlayTime();
         final float toValue = (direction == IN) ? mOriginalToValue : mOriginalFromValue;
-        final float startValue = mFirstRun ? mOriginalFromValue :
-                ((Float) mAnimator.getAnimatedValue()).floatValue();
+        final float startValue = mFirstRun ? mOriginalFromValue : mValue;
 
         // Make sure it's stopped before we modify any values
         cancel();
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index f63cce5..bde87cb 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -16,20 +16,37 @@
 
 package com.android.launcher3;
 
+import static com.android.launcher3.Utilities.getDevicePrefs;
+import static com.android.launcher3.config.FeatureFlags.APPLY_CONFIG_AT_RUNTIME;
+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;
 import android.content.res.XmlResourceParser;
 import android.graphics.Point;
-import android.support.annotation.VisibleForTesting;
+import android.graphics.Rect;
+import android.text.TextUtils;
+import android.util.AttributeSet;
 import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.TypedValue;
 import android.util.Xml;
 import android.view.Display;
 import android.view.WindowManager;
 
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.util.Thunk;
+import com.android.launcher3.graphics.IconShape;
+import com.android.launcher3.util.ConfigMonitor;
+import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.Themes;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -37,27 +54,36 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Comparator;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
 
 public class InvariantDeviceProfile {
 
-    // This is a static that we use for the default icon size on a 4/5-inch phone
-    private static float DEFAULT_ICON_SIZE_DP = 60;
+    public static final String TAG = "IDP";
+    // We do not need any synchronization for this variable as its only written on UI thread.
+    public static final MainThreadInitializedObject<InvariantDeviceProfile> INSTANCE =
+            new MainThreadInitializedObject<>(InvariantDeviceProfile::new);
+
+    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 float KNEARESTNEIGHBOR = 3;
-    private static float WEIGHT_POWER = 5;
+    private static final float KNEARESTNEIGHBOR = 3;
+    private static final float WEIGHT_POWER = 5;
 
     // used to offset float not being able to express extremely small weights in extreme cases.
-    private static float WEIGHT_EFFICIENT = 100000f;
+    private static final float WEIGHT_EFFICIENT = 100000f;
 
-    // Profile-defining invariant properties
-    String name;
-    float minWidthDps;
-    float minHeightDps;
+    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.
@@ -71,54 +97,81 @@
     public int numFolderRows;
     public int numFolderColumns;
     public float iconSize;
+    public String iconShapePath;
     public float landscapeIconSize;
     public int iconBitmapSize;
     public int fillResIconDpi;
     public float iconTextSize;
 
+    private SparseArray<TypedValue> mExtraAttrs;
+
     /**
      * Number of icons inside the hotseat area.
      */
     public int numHotseatIcons;
 
-    int defaultLayoutId;
+    public int defaultLayoutId;
     int demoModeLayoutId;
 
     public DeviceProfile landscapeProfile;
     public DeviceProfile portraitProfile;
 
     public Point defaultWallpaperSize;
+    public Rect defaultWidgetPadding;
+
+    private final ArrayList<OnIDPChangeListener> mChangeListeners = new ArrayList<>();
+    private ConfigMonitor mConfigMonitor;
+    private OverlayMonitor mOverlayMonitor;
 
     @VisibleForTesting
-    public InvariantDeviceProfile() {
-    }
+    public InvariantDeviceProfile() {}
 
     private InvariantDeviceProfile(InvariantDeviceProfile p) {
-        this(p.name, p.minWidthDps, p.minHeightDps, p.numRows, p.numColumns,
-                p.numFolderRows, p.numFolderColumns,
-                p.iconSize, p.landscapeIconSize, p.iconTextSize, p.numHotseatIcons,
-                p.defaultLayoutId, p.demoModeLayoutId);
-    }
-
-    private InvariantDeviceProfile(String n, float w, float h, int r, int c, int fr, int fc,
-            float is, float lis, float its, int hs, int dlId, int dmlId) {
-        name = n;
-        minWidthDps = w;
-        minHeightDps = h;
-        numRows = r;
-        numColumns = c;
-        numFolderRows = fr;
-        numFolderColumns = fc;
-        iconSize = is;
-        landscapeIconSize = lis;
-        iconTextSize = its;
-        numHotseatIcons = hs;
-        defaultLayoutId = dlId;
-        demoModeLayoutId = dmlId;
+        numRows = p.numRows;
+        numColumns = p.numColumns;
+        numFolderRows = p.numFolderRows;
+        numFolderColumns = p.numFolderColumns;
+        iconSize = p.iconSize;
+        iconShapePath = p.iconShapePath;
+        landscapeIconSize = p.landscapeIconSize;
+        iconTextSize = p.iconTextSize;
+        numHotseatIcons = p.numHotseatIcons;
+        defaultLayoutId = p.defaultLayoutId;
+        demoModeLayoutId = p.demoModeLayoutId;
+        mExtraAttrs = p.mExtraAttrs;
+        mOverlayMonitor = p.mOverlayMonitor;
     }
 
     @TargetApi(23)
-    public InvariantDeviceProfile(Context context) {
+    private InvariantDeviceProfile(Context context) {
+        initGrid(context, Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null));
+        mConfigMonitor = new ConfigMonitor(context,
+                APPLY_CONFIG_AT_RUNTIME.get() ? this::onConfigChanged : this::killProcess);
+        mOverlayMonitor = new OverlayMonitor(context);
+    }
+
+    /**
+     * This constructor should NOT have any monitors by design.
+     */
+    public InvariantDeviceProfile(Context context, String gridName) {
+        String newName = initGrid(context, gridName);
+        if (newName == null || !newName.equals(gridName)) {
+            throw new IllegalArgumentException("Unknown grid name");
+        }
+    }
+
+    /**
+     * 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) {
         WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
         Display display = wm.getDefaultDisplay();
         DisplayMetrics dm = new DisplayMetrics();
@@ -128,16 +181,18 @@
         Point largestSize = new Point();
         display.getCurrentSizeRange(smallestSize, largestSize);
 
+        ArrayList<DisplayOption> allOptions = getPredefinedDeviceProfiles(context, gridName);
         // This guarantees that width < height
-        minWidthDps = Utilities.dpiFromPx(Math.min(smallestSize.x, smallestSize.y), dm);
-        minHeightDps = Utilities.dpiFromPx(Math.min(largestSize.x, largestSize.y), dm);
+        float minWidthDps = Utilities.dpiFromPx(Math.min(smallestSize.x, smallestSize.y), dm);
+        float minHeightDps = Utilities.dpiFromPx(Math.min(largestSize.x, largestSize.y), dm);
+        // Sort the profiles based on the closeness to the device size
+        Collections.sort(allOptions, (a, b) ->
+                Float.compare(dist(minWidthDps, minHeightDps, a.minWidthDps, a.minHeightDps),
+                        dist(minWidthDps, minHeightDps, b.minWidthDps, b.minHeightDps)));
+        DisplayOption interpolatedDisplayOption =
+                invDistWeightedInterpolate(minWidthDps,  minHeightDps, allOptions);
 
-        ArrayList<InvariantDeviceProfile> closestProfiles = findClosestDeviceProfiles(
-                minWidthDps, minHeightDps, getPredefinedDeviceProfiles(context));
-        InvariantDeviceProfile interpolatedDeviceProfileOut =
-                invDistWeightedInterpolate(minWidthDps,  minHeightDps, closestProfiles);
-
-        InvariantDeviceProfile closestProfile = closestProfiles.get(0);
+        GridOption closestProfile = allOptions.get(0).grid;
         numRows = closestProfile.numRows;
         numColumns = closestProfile.numColumns;
         numHotseatIcons = closestProfile.numHotseatIcons;
@@ -145,11 +200,18 @@
         demoModeLayoutId = closestProfile.demoModeLayoutId;
         numFolderRows = closestProfile.numFolderRows;
         numFolderColumns = closestProfile.numFolderColumns;
+        mExtraAttrs = closestProfile.extraAttrs;
 
-        iconSize = interpolatedDeviceProfileOut.iconSize;
-        landscapeIconSize = interpolatedDeviceProfileOut.landscapeIconSize;
-        iconBitmapSize = Utilities.pxFromDp(iconSize, dm);
-        iconTextSize = interpolatedDeviceProfileOut.iconTextSize;
+        if (!closestProfile.name.equals(gridName)) {
+            Utilities.getPrefs(context).edit()
+                    .putString(KEY_IDP_GRID_NAME, closestProfile.name).apply();
+        }
+
+        iconSize = interpolatedDisplayOption.iconSize;
+        iconShapePath = getIconShapePath(context);
+        landscapeIconSize = interpolatedDisplayOption.landscapeIconSize;
+        iconBitmapSize = ResourceUtils.pxFromDp(iconSize, dm);
+        iconTextSize = interpolatedDisplayOption.iconTextSize;
         fillResIconDpi = getLauncherIconDensity(iconBitmapSize);
 
         // If the partner customization apk contains any grid overrides, apply them
@@ -177,43 +239,136 @@
         } else {
             defaultWallpaperSize = new Point(Math.max(smallSide * 2, largeSide), largeSide);
         }
+
+        ComponentName cn = new ComponentName(context.getPackageName(), getClass().getName());
+        defaultWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(context, cn, null);
+
+        return closestProfile.name;
     }
 
-    ArrayList<InvariantDeviceProfile> getPredefinedDeviceProfiles(Context context) {
-        ArrayList<InvariantDeviceProfile> profiles = new ArrayList<>();
+    @Nullable
+    public TypedValue getAttrValue(int attr) {
+        return mExtraAttrs == null ? null : mExtraAttrs.get(attr);
+    }
+
+    public void addOnChangeListener(OnIDPChangeListener listener) {
+        mChangeListeners.add(listener);
+    }
+
+    public void removeOnChangeListener(OnIDPChangeListener listener) {
+        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();
+        Utilities.getPrefs(appContext).edit().putString(KEY_IDP_GRID_NAME, gridName).apply();
+        new MainThreadExecutor().execute(() -> onConfigChanged(appContext));
+    }
+
+    private void onConfigChanged(Context context) {
+        // Config changes, what shall we do?
+        InvariantDeviceProfile oldProfile = new InvariantDeviceProfile(this);
+
+        // Re-init grid
+        // TODO(b/131867841): We pass in null here so that we can calculate the closest profile
+        // without the bias of the grid name.
+        initGrid(context, null);
+
+        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);
+        }
+    }
+
+    static ArrayList<DisplayOption> getPredefinedDeviceProfiles(Context context, String gridName) {
+        ArrayList<DisplayOption> profiles = new ArrayList<>();
         try (XmlResourceParser parser = context.getResources().getXml(R.xml.device_profiles)) {
             final int depth = parser.getDepth();
             int type;
-
             while (((type = parser.next()) != XmlPullParser.END_TAG ||
                     parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
-                if ((type == XmlPullParser.START_TAG) && "profile".equals(parser.getName())) {
-                    TypedArray a = context.obtainStyledAttributes(
-                            Xml.asAttributeSet(parser), R.styleable.InvariantDeviceProfile);
-                    int numRows = a.getInt(R.styleable.InvariantDeviceProfile_numRows, 0);
-                    int numColumns = a.getInt(R.styleable.InvariantDeviceProfile_numColumns, 0);
-                    float iconSize = a.getFloat(R.styleable.InvariantDeviceProfile_iconSize, 0);
-                    profiles.add(new InvariantDeviceProfile(
-                            a.getString(R.styleable.InvariantDeviceProfile_name),
-                            a.getFloat(R.styleable.InvariantDeviceProfile_minWidthDps, 0),
-                            a.getFloat(R.styleable.InvariantDeviceProfile_minHeightDps, 0),
-                            numRows,
-                            numColumns,
-                            a.getInt(R.styleable.InvariantDeviceProfile_numFolderRows, numRows),
-                            a.getInt(R.styleable.InvariantDeviceProfile_numFolderColumns, numColumns),
-                            iconSize,
-                            a.getFloat(R.styleable.InvariantDeviceProfile_landscapeIconSize, iconSize),
-                            a.getFloat(R.styleable.InvariantDeviceProfile_iconTextSize, 0),
-                            a.getInt(R.styleable.InvariantDeviceProfile_numHotseatIcons, numColumns),
-                            a.getResourceId(R.styleable.InvariantDeviceProfile_defaultLayoutId, 0),
-                            a.getResourceId(R.styleable.InvariantDeviceProfile_demoModeLayoutId, 0)));
-                    a.recycle();
+                if ((type == XmlPullParser.START_TAG)
+                        && GridOption.TAG_NAME.equals(parser.getName())) {
+
+                    GridOption gridOption = new GridOption(context, Xml.asAttributeSet(parser));
+                    final int displayDepth = parser.getDepth();
+                    while (((type = parser.next()) != XmlPullParser.END_TAG ||
+                            parser.getDepth() > displayDepth)
+                            && type != XmlPullParser.END_DOCUMENT) {
+                        if ((type == XmlPullParser.START_TAG) && "display-option".equals(
+                                parser.getName())) {
+                            profiles.add(new DisplayOption(
+                                    gridOption, context, Xml.asAttributeSet(parser)));
+                        }
+                    }
                 }
             }
         } catch (IOException|XmlPullParserException e) {
             throw new RuntimeException(e);
         }
-        return profiles;
+
+        ArrayList<DisplayOption> filteredProfiles = new ArrayList<>();
+        if (!TextUtils.isEmpty(gridName)) {
+            for (DisplayOption option : profiles) {
+                if (gridName.equals(option.grid.name)) {
+                    filteredProfiles.add(option);
+                }
+            }
+        }
+        if (filteredProfiles.isEmpty()) {
+            // No grid found, use the default options
+            for (DisplayOption option : profiles) {
+                if (option.canBeDefault) {
+                    filteredProfiles.add(option);
+                }
+            }
+        }
+        if (filteredProfiles.isEmpty()) {
+            throw new RuntimeException("No display option with canBeDefault=true");
+        }
+        return filteredProfiles;
     }
 
     private int getLauncherIconDensity(int requiredSize) {
@@ -252,71 +407,28 @@
         }
     }
 
-    @Thunk float dist(float x0, float y0, float x1, float y1) {
+    private static float dist(float x0, float y0, float x1, float y1) {
         return (float) Math.hypot(x1 - x0, y1 - y0);
     }
 
-    /**
-     * Returns the closest device profiles ordered by closeness to the specified width and height
-     */
-    // Package private visibility for testing.
-    ArrayList<InvariantDeviceProfile> findClosestDeviceProfiles(
-            final float width, final float height, ArrayList<InvariantDeviceProfile> points) {
-
-        // Sort the profiles by their closeness to the dimensions
-        ArrayList<InvariantDeviceProfile> pointsByNearness = points;
-        Collections.sort(pointsByNearness, new Comparator<InvariantDeviceProfile>() {
-            public int compare(InvariantDeviceProfile a, InvariantDeviceProfile b) {
-                return Float.compare(dist(width, height, a.minWidthDps, a.minHeightDps),
-                        dist(width, height, b.minWidthDps, b.minHeightDps));
-            }
-        });
-
-        return pointsByNearness;
-    }
-
-    // Package private visibility for testing.
-    InvariantDeviceProfile invDistWeightedInterpolate(float width, float height,
-                ArrayList<InvariantDeviceProfile> points) {
+    @VisibleForTesting
+    static DisplayOption invDistWeightedInterpolate(float width, float height,
+                ArrayList<DisplayOption> points) {
         float weights = 0;
 
-        InvariantDeviceProfile p = points.get(0);
+        DisplayOption p = points.get(0);
         if (dist(width, height, p.minWidthDps, p.minHeightDps) == 0) {
             return p;
         }
 
-        InvariantDeviceProfile out = new InvariantDeviceProfile();
+        DisplayOption out = new DisplayOption();
         for (int i = 0; i < points.size() && i < KNEARESTNEIGHBOR; ++i) {
-            p = new InvariantDeviceProfile(points.get(i));
+            p = points.get(i);
             float w = weight(width, height, p.minWidthDps, p.minHeightDps, WEIGHT_POWER);
             weights += w;
-            out.add(p.multiply(w));
+            out.add(new DisplayOption().add(p).multiply(w));
         }
-        return out.multiply(1.0f/weights);
-    }
-
-    private void add(InvariantDeviceProfile p) {
-        iconSize += p.iconSize;
-        landscapeIconSize += p.landscapeIconSize;
-        iconTextSize += p.iconTextSize;
-    }
-
-    private InvariantDeviceProfile multiply(float w) {
-        iconSize *= w;
-        landscapeIconSize *= w;
-        iconTextSize *= w;
-        return this;
-    }
-
-    public int getAllAppsButtonRank() {
-        if (FeatureFlags.IS_DOGFOOD_BUILD && FeatureFlags.NO_ALL_APPS_ICON) {
-            throw new IllegalAccessError("Accessing all apps rank when all-apps is disabled");
-        }
-        return numHotseatIcons / 2;
-    }
-
-    public boolean isAllAppsButtonRank(int rank) {
-        return rank == getAllAppsButtonRank();
+        return out.multiply(1.0f / weights);
     }
 
     public DeviceProfile getDeviceProfile(Context context) {
@@ -324,7 +436,7 @@
                 == Configuration.ORIENTATION_LANDSCAPE ? landscapeProfile : portraitProfile;
     }
 
-    private float weight(float x0, float y0, float x1, float y1, float pow) {
+    private static float weight(float x0, float y0, float x1, float y1, float pow) {
         float d = dist(x0, y0, x1, y1);
         if (Float.compare(d, 0f) == 0) {
             return Float.POSITIVE_INFINITY;
@@ -361,4 +473,119 @@
         return x * aspectRatio + y;
     }
 
+    public interface OnIDPChangeListener {
+
+        void onIdpChanged(int changeFlags, InvariantDeviceProfile profile);
+    }
+
+
+    public static final class GridOption {
+
+        public static final String TAG_NAME = "grid-option";
+
+        public final String name;
+        public final int numRows;
+        public final int numColumns;
+
+        private final int numFolderRows;
+        private final int numFolderColumns;
+
+        private final int numHotseatIcons;
+
+        private final int defaultLayoutId;
+        private final int demoModeLayoutId;
+
+        private final SparseArray<TypedValue> extraAttrs;
+
+        public GridOption(Context context, AttributeSet attrs) {
+            TypedArray a = context.obtainStyledAttributes(
+                    attrs, R.styleable.GridDisplayOption);
+            name = a.getString(R.styleable.GridDisplayOption_name);
+            numRows = a.getInt(R.styleable.GridDisplayOption_numRows, 0);
+            numColumns = a.getInt(R.styleable.GridDisplayOption_numColumns, 0);
+
+            defaultLayoutId = a.getResourceId(
+                    R.styleable.GridDisplayOption_defaultLayoutId, 0);
+            demoModeLayoutId = a.getResourceId(
+                    R.styleable.GridDisplayOption_demoModeLayoutId, defaultLayoutId);
+            numHotseatIcons = a.getInt(
+                    R.styleable.GridDisplayOption_numHotseatIcons, numColumns);
+            numFolderRows = a.getInt(
+                    R.styleable.GridDisplayOption_numFolderRows, numRows);
+            numFolderColumns = a.getInt(
+                    R.styleable.GridDisplayOption_numFolderColumns, numColumns);
+            a.recycle();
+
+            extraAttrs = Themes.createValueMap(context, attrs,
+                    IntArray.wrap(R.styleable.GridDisplayOption));
+        }
+    }
+
+    private static final class DisplayOption {
+        private final GridOption grid;
+
+        private final String name;
+        private final float minWidthDps;
+        private final float minHeightDps;
+        private final boolean canBeDefault;
+
+        private float iconSize;
+        private float landscapeIconSize;
+        private float iconTextSize;
+
+        DisplayOption(GridOption grid, Context context, AttributeSet attrs) {
+            this.grid = grid;
+
+            TypedArray a = context.obtainStyledAttributes(
+                    attrs, R.styleable.ProfileDisplayOption);
+
+            name = a.getString(R.styleable.ProfileDisplayOption_name);
+            minWidthDps = a.getFloat(R.styleable.ProfileDisplayOption_minWidthDps, 0);
+            minHeightDps = a.getFloat(R.styleable.ProfileDisplayOption_minHeightDps, 0);
+            canBeDefault = a.getBoolean(
+                    R.styleable.ProfileDisplayOption_canBeDefault, false);
+
+            iconSize = a.getFloat(R.styleable.ProfileDisplayOption_iconImageSize, 0);
+            landscapeIconSize = a.getFloat(R.styleable.ProfileDisplayOption_landscapeIconSize,
+                    iconSize);
+            iconTextSize = a.getFloat(R.styleable.ProfileDisplayOption_iconTextSize, 0);
+            a.recycle();
+        }
+
+        DisplayOption() {
+            grid = null;
+            name = null;
+            minWidthDps = 0;
+            minHeightDps = 0;
+            canBeDefault = false;
+        }
+
+        private DisplayOption multiply(float w) {
+            iconSize *= w;
+            landscapeIconSize *= w;
+            iconTextSize *= w;
+            return this;
+        }
+
+        private DisplayOption add(DisplayOption p) {
+            iconSize += p.iconSize;
+            landscapeIconSize += p.landscapeIconSize;
+            iconTextSize += p.iconTextSize;
+            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);
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java
index fa3253c..134e116 100644
--- a/src/com/android/launcher3/ItemInfo.java
+++ b/src/com/android/launcher3/ItemInfo.java
@@ -34,7 +34,7 @@
     /**
      * The id in the settings database for this item
      */
-    public long id = NO_ID;
+    public int id = NO_ID;
 
     /**
      * One of {@link LauncherSettings.Favorites#ITEM_TYPE_APPLICATION},
@@ -52,14 +52,14 @@
      * will be {@link #NO_ID} (since it is not stored in the settings DB). For user folders
      * it will be the id of the folder.
      */
-    public long container = NO_ID;
+    public int container = NO_ID;
 
     /**
      * Indicates the screen in which the shortcut appears if the container types is
      * {@link LauncherSettings.Favorites#CONTAINER_DESKTOP}. (i.e., ignore if the container type is
      * {@link LauncherSettings.Favorites#CONTAINER_HOTSEAT})
      */
-    public long screenId = -1;
+    public int screenId = -1;
 
     /**
      * Indicates the X position of the associated cell.
@@ -114,8 +114,6 @@
 
     ItemInfo(ItemInfo info) {
         copyFrom(info);
-        // tempdebug:
-        LauncherModel.checkItemInfo(this);
     }
 
     public void copyFrom(ItemInfo info) {
@@ -158,8 +156,8 @@
 
     public void readFromValues(ContentValues values) {
         itemType = values.getAsInteger(LauncherSettings.Favorites.ITEM_TYPE);
-        container = values.getAsLong(LauncherSettings.Favorites.CONTAINER);
-        screenId = values.getAsLong(LauncherSettings.Favorites.SCREEN);
+        container = values.getAsInteger(LauncherSettings.Favorites.CONTAINER);
+        screenId = values.getAsInteger(LauncherSettings.Favorites.SCREEN);
         cellX = values.getAsInteger(LauncherSettings.Favorites.CELLX);
         cellY = values.getAsInteger(LauncherSettings.Favorites.CELLY);
         spanX = values.getAsInteger(LauncherSettings.Favorites.SPANX);
@@ -204,4 +202,13 @@
     public boolean isDisabled() {
         return false;
     }
+
+    public int getViewId() {
+        // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
+        // This cast is safe as long as the id < 0x00FFFFFF
+        // Since we jail all the dynamically generated views, there should be no clashes
+        // with any other views.
+        return id;
+    }
+
 }
diff --git a/src/com/android/launcher3/ItemInfoWithIcon.java b/src/com/android/launcher3/ItemInfoWithIcon.java
index 4677d31..e29f927 100644
--- a/src/com/android/launcher3/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/ItemInfoWithIcon.java
@@ -16,8 +16,12 @@
 
 package com.android.launcher3;
 
+import static com.android.launcher3.icons.BitmapInfo.LOW_RES_ICON;
+
 import android.graphics.Bitmap;
 
+import com.android.launcher3.icons.BitmapInfo;
+
 /**
  * Represents an ItemInfo which also holds an icon.
  */
@@ -34,11 +38,6 @@
     public int iconColor;
 
     /**
-     * Indicates whether we're using a low res icon
-     */
-    public boolean usingLowResIcon;
-
-    /**
      * Indicates that the icon is disabled due to safe mode restrictions.
      */
     public static final int FLAG_DISABLED_SAFEMODE = 1 << 0;
@@ -107,7 +106,6 @@
         super(info);
         iconBitmap = info.iconBitmap;
         iconColor = info.iconColor;
-        usingLowResIcon = info.usingLowResIcon;
         runtimeStatusFlags = info.runtimeStatusFlags;
     }
 
@@ -115,4 +113,17 @@
     public boolean isDisabled() {
         return (runtimeStatusFlags & FLAG_DISABLED_MASK) != 0;
     }
+
+    /**
+     * Indicates whether we're using a low res icon
+     */
+    public boolean usingLowResIcon() {
+        return iconBitmap == LOW_RES_ICON;
+    }
+
+    public void applyFrom(BitmapInfo info) {
+        iconBitmap = info.icon;
+        iconColor = info.color;
+    }
+
 }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 4fe3503..bc3aa7e 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -16,15 +16,22 @@
 
 package com.android.launcher3;
 
+import static android.content.pm.ActivityInfo.CONFIG_LOCALE;
 import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
 
+import static com.android.launcher3.AbstractFloatingView.TYPE_SNACKBAR;
 import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
 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.dragndrop.DragLayer.ALPHA_INDEX_LAUNCHER_LOAD;
 import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
 import static com.android.launcher3.logging.LoggerUtils.newTarget;
+import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
+import static com.android.launcher3.util.RaceConditionTracker.ENTER;
+import static com.android.launcher3.util.RaceConditionTracker.EXIT;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -39,7 +46,6 @@
 import android.content.BroadcastReceiver;
 import android.content.ComponentCallbacks2;
 import android.content.Context;
-import android.content.ContextWrapper;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.IntentSender;
@@ -48,15 +54,13 @@
 import android.content.res.Configuration;
 import android.database.sqlite.SQLiteDatabase;
 import android.graphics.Point;
-import android.os.AsyncTask;
+import android.graphics.Rect;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Parcelable;
 import android.os.Process;
 import android.os.StrictMode;
-import android.os.UserHandle;
-import android.support.annotation.Nullable;
 import android.text.TextUtils;
 import android.text.method.TextKeyListener;
 import android.util.Log;
@@ -74,25 +78,31 @@
 import android.widget.Toast;
 
 import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.Workspace.ItemOperator;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.allapps.AllAppsStore;
 import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.allapps.DiscoveryBounce;
-import com.android.launcher3.badge.BadgeInfo;
+import com.android.launcher3.anim.PropertyListBuilder;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.LauncherAppsCompatVO;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dot.DotInfo;
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragView;
+import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.folder.FolderIconPreviewVerifier;
+import com.android.launcher3.graphics.RotationMode;
+import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.keyboard.CustomActionsPopup;
 import com.android.launcher3.keyboard.ViewGroupFocusHelper;
 import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.logging.StatsLogUtils;
 import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.logging.UserEventDispatcher.UserEventDelegate;
+import com.android.launcher3.model.AppLaunchTracker;
 import com.android.launcher3.model.ModelWriter;
 import com.android.launcher3.notification.NotificationListener;
 import com.android.launcher3.popup.PopupContainerWithArrow;
@@ -100,6 +110,7 @@
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.states.InternalStateHandler;
 import com.android.launcher3.states.RotationHelper;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.uioverrides.UiFactory;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -108,20 +119,23 @@
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.util.ActivityResultInfo;
 import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.PendingRequestArgs;
+import com.android.launcher3.util.RaceConditionTracker;
 import com.android.launcher3.util.SystemUiController;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.util.TraceHelper;
 import com.android.launcher3.util.UiThreadHelper;
 import com.android.launcher3.util.ViewOnDrawExecutor;
+import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.OptionsPopupView;
+import com.android.launcher3.views.ScrimView;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
 import com.android.launcher3.widget.PendingAddShortcutInfo;
 import com.android.launcher3.widget.PendingAddWidgetInfo;
@@ -136,15 +150,19 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
+import java.util.function.Predicate;
+
+import androidx.annotation.Nullable;
 
 /**
  * Default launcher application.
  */
 public class Launcher extends BaseDraggingActivity implements LauncherExterns,
-        LauncherModel.Callbacks, LauncherProviderChangeListener, UserEventDelegate{
+        LauncherModel.Callbacks, LauncherProviderChangeListener, UserEventDelegate,
+        InvariantDeviceProfile.OnIDPChangeListener {
     public static final String TAG = "Launcher";
     static final boolean LOGD = false;
 
@@ -175,10 +193,15 @@
     private static final String RUNTIME_STATE = "launcher.state";
     // Type: PendingRequestArgs
     private static final String RUNTIME_STATE_PENDING_REQUEST_ARGS = "launcher.request_args";
+    // Type: int
+    private static final String RUNTIME_STATE_PENDING_REQUEST_CODE = "launcher.request_code";
     // Type: ActivityResultInfo
     private static final String RUNTIME_STATE_PENDING_ACTIVITY_RESULT = "launcher.activity_result";
     // Type: SparseArray<Parcelable>
     private static final String RUNTIME_STATE_WIDGET_PANEL = "launcher.widget_panel";
+    public static final String ON_CREATE_EVT = "Launcher.onCreate";
+    private static final String ON_START_EVT = "Launcher.onStart";
+    private static final String ON_RESUME_EVT = "Launcher.onResume";
 
     private LauncherStateManager mStateManager;
 
@@ -189,6 +212,9 @@
     private static final int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
     @Thunk 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 LauncherAppTransitionManager mAppTransitionManager;
     private Configuration mOldConfig;
 
@@ -203,7 +229,6 @@
     private final int[] mTmpAddItemCellCoordinates = new int[2];
 
     @Thunk Hotseat mHotseat;
-    @Nullable private View mHotseatSearchBox;
 
     private DropTargetBar mDropTargetBar;
 
@@ -211,12 +236,15 @@
     @Thunk AllAppsContainerView mAppsView;
     AllAppsTransitionController mAllAppsController;
 
+    // Scrim view for the all apps and overview state.
+    @Thunk ScrimView mScrimView;
+
     // UI and state for the overview panel
     private View mOverviewPanel;
 
     @Thunk boolean mWorkspaceLoading = true;
 
-    private OnResumeCallback mOnResumeCallback;
+    private ArrayList<OnResumeCallback> mOnResumeCallbacks = new ArrayList<>();
 
     private ViewOnDrawExecutor mPendingExecutor;
 
@@ -240,17 +268,26 @@
      * {@link #startActivityForResult(Intent, int)} or {@link #requestPermissions(String[], int)}
      */
     private PendingRequestArgs mPendingRequestArgs;
+    // Request id for any pending activity result
+    private int mPendingActivityRequestCode = -1;
 
     public ViewGroupFocusHelper mFocusHandler;
 
     private RotationHelper mRotationHelper;
+    private Runnable mCancelTouchController;
 
+    final Handler mHandler = new Handler();
+    private final Runnable mHandleDeferredResume = this::handleDeferredResume;
+    private boolean mDeferredResumePending;
 
-    private final Handler mHandler = new Handler();
-    private final Runnable mLogOnDelayedResume = this::logOnDelayedResume;
+    private float mCurrentAssistantVisibility = 0f;
+
+    private DeviceProfile mStableDeviceProfile;
+    private RotationMode mRotationMode = RotationMode.NORMAL;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
+        RaceConditionTracker.onEvent(ON_CREATE_EVT, ENTER);
         if (DEBUG_STRICT_MODE) {
             StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                     .detectDiskReads()
@@ -273,8 +310,10 @@
         LauncherAppState app = LauncherAppState.getInstance(this);
         mOldConfig = new Configuration(getResources().getConfiguration());
         mModel = app.setLauncher(this);
-        initDeviceProfile(app.getInvariantDeviceProfile());
-
+        mRotationHelper = new RotationHelper(this);
+        InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
+        initDeviceProfile(idp);
+        idp.addOnChangeListener(this);
         mSharedPrefs = Utilities.getPrefs(this);
         mIconCache = app.getIconCache();
         mAccessibilityDelegate = new LauncherAccessibilityDelegate(this);
@@ -294,7 +333,6 @@
         setupViews();
         mPopupDataProvider = new PopupDataProvider(this);
 
-        mRotationHelper = new RotationHelper(this);
         mAppTransitionManager = LauncherAppTransitionManager.newInstance(this);
 
         boolean internalStateHandled = InternalStateHandler.handleCreate(this, getIntent());
@@ -306,6 +344,7 @@
             }
         }
         restoreState(savedInstanceState);
+        mStateManager.reapplyState();
 
         // We only load the page synchronously if the user rotates (or triggers a
         // configuration change) while launcher is in the foreground
@@ -345,26 +384,45 @@
         mRotationHelper.initialize();
 
         TraceHelper.endSection("Launcher-onCreate");
+        RaceConditionTracker.onEvent(ON_CREATE_EVT, EXIT);
+        mStateManager.addStateListener(new LauncherStateManager.StateListener() {
+            @Override
+            public void onStateTransitionStart(LauncherState toState) {}
+
+            @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);
+                }
+            }
+        });
     }
 
     @Override
     public void onEnterAnimationComplete() {
         super.onEnterAnimationComplete();
         UiFactory.onEnterAnimationComplete(this);
+        mAllAppsController.highlightWorkTabIfNecessary();
+        mRotationHelper.setCurrentTransitionRequest(REQUEST_NONE);
     }
 
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
         int diff = newConfig.diff(mOldConfig);
-        if ((diff & (CONFIG_ORIENTATION | CONFIG_SCREEN_SIZE)) != 0) {
-            mUserEventDispatcher = null;
-            initDeviceProfile(mDeviceProfile.inv);
-            dispatchDeviceProfileChanged();
-            reapplyUi();
-            mDragLayer.recreateControllers();
 
-            // TODO: We can probably avoid rebind when only screen size changed.
-            rebindModel();
+        if ((diff & CONFIG_LOCALE) != 0) {
+            Folder.setLocaleDependentFields(getResources(), true /* force */);
+        }
+
+        if ((diff & (CONFIG_ORIENTATION | CONFIG_SCREEN_SIZE)) != 0) {
+            onIdpChanged(mDeviceProfile.inv);
         }
 
         mOldConfig.setTo(newConfig);
@@ -372,8 +430,20 @@
         super.onConfigurationChanged(newConfig);
     }
 
+    public void reload() {
+        onIdpChanged(mDeviceProfile.inv);
+    }
+
+    private boolean supportsFakeLandscapeUI() {
+        return FeatureFlags.FAKE_LANDSCAPE_UI.get() && !mRotationHelper.homeScreenCanRotate();
+    }
+
     @Override
-    protected void reapplyUi() {
+    public void reapplyUi() {
+        if (supportsFakeLandscapeUI()) {
+            mRotationMode = mStableDeviceProfile == null
+                    ? RotationMode.NORMAL : UiFactory.getRotationMode(mDeviceProfile);
+        }
         getRootView().dispatchInsets();
         getStateManager().reapplyState(true /* cancelCurrentAnimation */);
     }
@@ -387,17 +457,85 @@
         }
     }
 
+    @Override
+    public void onIdpChanged(int changeFlags, InvariantDeviceProfile idp) {
+        onIdpChanged(idp);
+    }
+
+    private void onIdpChanged(InvariantDeviceProfile idp) {
+        mUserEventDispatcher = null;
+
+        DeviceProfile oldWallpaperProfile = getWallpaperDeviceProfile();
+        initDeviceProfile(idp);
+        dispatchDeviceProfileChanged();
+        reapplyUi();
+        mDragLayer.recreateControllers();
+
+        // Calling onSaveInstanceState ensures that static cache used by listWidgets is
+        // initialized properly.
+        onSaveInstanceState(new Bundle());
+        if (oldWallpaperProfile != getWallpaperDeviceProfile()) {
+            rebindModel();
+        }
+    }
+
+    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);
+        }
+    }
+
     private void initDeviceProfile(InvariantDeviceProfile idp) {
         // Load configuration-specific DeviceProfile
         mDeviceProfile = idp.getDeviceProfile(this);
-        if (isInMultiWindowModeCompat()) {
+        if (isInMultiWindowMode()) {
             Display display = getWindowManager().getDefaultDisplay();
             Point mwSize = new Point();
             display.getSize(mwSize);
             mDeviceProfile = mDeviceProfile.getMultiWindowProfile(this, mwSize);
         }
+
+        if (supportsFakeLandscapeUI() && mDeviceProfile.isVerticalBarLayout()) {
+            mStableDeviceProfile = mDeviceProfile.inv.portraitProfile;
+            mRotationMode = UiFactory.getRotationMode(mDeviceProfile);
+        } else {
+            mStableDeviceProfile = null;
+            mRotationMode = RotationMode.NORMAL;
+        }
+
+        mRotationHelper.updateRotationAnimation();
         onDeviceProfileInitiated();
-        mModelWriter = mModel.getWriter(mDeviceProfile.isVerticalBarLayout(), true);
+        mModelWriter = mModel.getWriter(getWallpaperDeviceProfile().isVerticalBarLayout(), true);
+    }
+
+    public void updateInsets(Rect insets) {
+        mDeviceProfile.updateInsets(insets);
+        if (mStableDeviceProfile != null) {
+            Rect r = mStableDeviceProfile.getInsets();
+            mRotationMode.mapInsets(this, insets, r);
+            mStableDeviceProfile.updateInsets(r);
+        }
+    }
+
+    @Override
+    public RotationMode getRotationMode() {
+        return mRotationMode;
+    }
+
+    /**
+     * Device profile to be used by UI elements which are shown directly on top of the wallpaper
+     * and whose presentation is tied to the wallpaper (and physical device) and not the activity
+     * configuration.
+     */
+    @Override
+    public DeviceProfile getWallpaperDeviceProfile() {
+        return mStableDeviceProfile == null ? mDeviceProfile : mStableDeviceProfile;
     }
 
     public RotationHelper getRotationHelper() {
@@ -450,30 +588,26 @@
         return !isWorkspaceLoading();
     }
 
-    public int getViewIdForItem(ItemInfo info) {
-        // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
-        // This cast is safe as long as the id < 0x00FFFFFF
-        // Since we jail all the dynamically generated views, there should be no clashes
-        // with any other views.
-        return (int) info.id;
-    }
-
     public PopupDataProvider getPopupDataProvider() {
         return mPopupDataProvider;
     }
 
     @Override
-    public BadgeInfo getBadgeInfoForItem(ItemInfo info) {
-        return mPopupDataProvider.getBadgeInfoForItem(info);
+    public DotInfo getDotInfoForItem(ItemInfo info) {
+        return mPopupDataProvider.getDotInfoForItem(info);
     }
 
     @Override
     public void invalidateParent(ItemInfo info) {
-        FolderIconPreviewVerifier verifier = new FolderIconPreviewVerifier(getDeviceProfile().inv);
-        if (verifier.isItemInPreview(info.rank) && (info.container >= 0)) {
+        if (info.container >= 0) {
             View folderIcon = getWorkspace().getHomescreenIconByItemId(info.container);
-            if (folderIcon != null) {
-                folderIcon.invalidate();
+            if (folderIcon instanceof FolderIcon && folderIcon.getTag() instanceof FolderInfo) {
+                FolderIconPreviewVerifier verifier =
+                        new FolderIconPreviewVerifier(getDeviceProfile().inv);
+                verifier.setFolderInfo((FolderInfo) folderIcon.getTag());
+                if (verifier.isItemInPreview(info.rank)) {
+                    folderIcon.invalidate();
+                }
             }
         }
     }
@@ -482,9 +616,9 @@
      * Returns whether we should delay spring loaded mode -- for shortcuts and widgets that have
      * a configuration step, this allows the proper animations to run after other transitions.
      */
-    private long completeAdd(
+    private int completeAdd(
             int requestCode, Intent intent, int appWidgetId, PendingRequestArgs info) {
-        long screenId = info.screenId;
+        int screenId = info.screenId;
         if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
             // When the screen id represents an actual screen (as opposed to a rank) we make sure
             // that the drop page actually exists.
@@ -644,6 +778,7 @@
     @Override
     public void onActivityResult(
             final int requestCode, final int resultCode, final Intent data) {
+        mPendingActivityRequestCode = -1;
         handleActivityResult(requestCode, resultCode, data);
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onActivityResult(requestCode, resultCode, data);
@@ -667,7 +802,7 @@
 
             if (grantResults.length > 0
                     && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
-                startActivitySafely(v, intent, null);
+                startActivitySafely(v, intent, null, null);
             } else {
                 // TODO: Show a snack bar with link to settings
                 Toast.makeText(this, getString(R.string.msg_no_phone_permission,
@@ -686,7 +821,7 @@
      * @param screenId the screen id to check
      * @return the new screen, or screenId if it exists
      */
-    private long ensurePendingDropLayoutExists(long screenId) {
+    private int ensurePendingDropLayoutExists(int screenId) {
         CellLayout dropLayout = mWorkspace.getScreenWithId(screenId);
         if (dropLayout == null) {
             // it's possible that the add screen was removed because it was
@@ -734,11 +869,11 @@
     @Override
     protected void onStop() {
         super.onStop();
-        FirstFrameAnimatorHelper.setIsVisible(false);
 
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onStop();
         }
+
         getUserEventDispatcher().logActionCommand(Action.Command.STOP,
                 mStateManager.getState().containerType, -1);
 
@@ -755,49 +890,83 @@
 
     @Override
     protected void onStart() {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_OVERVIEW_EVENT_TAG, "Launcher.onStart");
+        }
+        RaceConditionTracker.onEvent(ON_START_EVT, ENTER);
         super.onStart();
-        FirstFrameAnimatorHelper.setIsVisible(true);
-
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onStart();
         }
         mAppWidgetHost.setListenIfResumed(true);
-        NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
-        UiFactory.onStart(this);
+        RaceConditionTracker.onEvent(ON_START_EVT, EXIT);
     }
 
-    private void logOnDelayedResume() {
-        if (hasBeenResumed()) {
+    private void handleDeferredResume() {
+        if (hasBeenResumed() && !mStateManager.getState().disableInteraction) {
             getUserEventDispatcher().logActionCommand(Action.Command.RESUME,
                     mStateManager.getState().containerType, -1);
             getUserEventDispatcher().startSession();
+
+            UiFactory.onLauncherStateOrResumeChanged(this);
+            AppLaunchTracker.INSTANCE.get(this).onReturnedToHome();
+
+            // Process any items that were added while Launcher was away.
+            InstallShortcutReceiver.disableAndFlushInstallQueue(
+                    InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED, this);
+
+            // Refresh shortcuts if the permission changed.
+            mModel.refreshShortcutsIfRequired();
+
+            // Set the notification listener and fetch updated notifications when we resume
+            NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
+
+            DiscoveryBounce.showForHomeIfNeeded(this);
+
+            if (mPendingActivityRequestCode != -1 && isInState(NORMAL)) {
+                UiFactory.resetPendingActivityResults(this, mPendingActivityRequestCode);
+            }
+            mDeferredResumePending = false;
+        } else {
+            mDeferredResumePending = true;
+        }
+    }
+
+    protected void onStateSet(LauncherState state) {
+        getAppWidgetHost().setResumed(state == LauncherState.NORMAL);
+        if (mDeferredResumePending) {
+            handleDeferredResume();
+        }
+        if (mLauncherCallbacks != null) {
+            mLauncherCallbacks.onStateChanged();
         }
     }
 
     @Override
     protected void onResume() {
+        RaceConditionTracker.onEvent(ON_RESUME_EVT, ENTER);
         TraceHelper.beginSection("ON_RESUME");
         super.onResume();
         TraceHelper.partitionSection("ON_RESUME", "superCall");
 
-        mHandler.removeCallbacks(mLogOnDelayedResume);
-        Utilities.postAsyncCallback(mHandler, mLogOnDelayedResume);
+        mHandler.removeCallbacks(mHandleDeferredResume);
+        Utilities.postAsyncCallback(mHandler, mHandleDeferredResume);
 
-        setOnResumeCallback(null);
-        // Process any items that were added while Launcher was away.
-        InstallShortcutReceiver.disableAndFlushInstallQueue(
-                InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED, this);
+        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();
+        }
 
-        // Refresh shortcuts if the permission changed.
-        mModel.refreshShortcutsIfRequired();
-
-        DiscoveryBounce.showForHomeIfNeeded(this);
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onResume();
         }
-        UiFactory.onLauncherStateOrResumeChanged(this);
 
         TraceHelper.endSection("ON_RESUME");
+        RaceConditionTracker.onEvent(ON_RESUME_EVT, EXIT);
     }
 
     @Override
@@ -808,7 +977,7 @@
         super.onPause();
         mDragController.cancelDrag();
         mDragController.resetLastGestureUpTime();
-
+        mDropTargetBar.animateToVisibility(false);
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onPause();
         }
@@ -864,17 +1033,6 @@
         }
     }
 
-    public boolean hasSettings() {
-        if (mLauncherCallbacks != null) {
-            return mLauncherCallbacks.hasSettings();
-        } else {
-            // On O and above we there is always some setting present settings (add icon to
-            // home screen or icon badging). On earlier APIs we will have the allow rotation
-            // setting, on devices with a locked orientation,
-            return Utilities.ATLEAST_OREO || !getResources().getBoolean(R.bool.allow_rotation);
-        }
-    }
-
     public boolean isInState(LauncherState state) {
         return mStateManager.getState() == state;
     }
@@ -900,6 +1058,7 @@
         if (requestArgs != null) {
             setWaitingForResult(requestArgs);
         }
+        mPendingActivityRequestCode = savedState.getInt(RUNTIME_STATE_PENDING_REQUEST_CODE);
 
         mPendingActivityResult = savedState.getParcelable(RUNTIME_STATE_PENDING_ACTIVITY_RESULT);
 
@@ -920,7 +1079,6 @@
         mWorkspace.initParentViews(mDragLayer);
         mOverviewPanel = findViewById(R.id.overview_panel);
         mHotseat = findViewById(R.id.hotseat);
-        mHotseatSearchBox = findViewById(R.id.search_container_hotseat);
 
         mLauncherView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
@@ -928,7 +1086,7 @@
 
         // Setup the drag layer
         mDragLayer.setup(mDragController, mWorkspace);
-        UiFactory.setOnTouchControllersChangedListener(this, mDragLayer::recreateControllers);
+        mCancelTouchController = UiFactory.enableLiveUIChanges(this);
 
         mWorkspace.setup(mDragController);
         // Until the workspace is bound, ensure that we keep the wallpaper offset locked to the
@@ -943,6 +1101,9 @@
         // Setup Apps
         mAppsView = findViewById(R.id.apps_view);
 
+        // Setup Scrim
+        mScrimView = findViewById(R.id.scrim_view);
+
         // Setup the drag controller (drop targets have to be added in reverse order in priority)
         mDragController.setMoveTarget(mWorkspace);
         mDropTargetBar.setup(mDragController);
@@ -955,7 +1116,7 @@
      *
      * @param info The data structure describing the shortcut.
      */
-    View createShortcut(ShortcutInfo info) {
+    View createShortcut(WorkspaceItemInfo info) {
         return createShortcut((ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()), info);
     }
 
@@ -967,10 +1128,10 @@
      *
      * @return A View inflated from layoutResId.
      */
-    public View createShortcut(ViewGroup parent, ShortcutInfo info) {
+    public View createShortcut(ViewGroup parent, WorkspaceItemInfo info) {
         BubbleTextView favorite = (BubbleTextView) LayoutInflater.from(parent.getContext())
                 .inflate(R.layout.app_icon, parent, false);
-        favorite.applyFromShortcutInfo(info);
+        favorite.applyFromWorkspaceItem(info);
         favorite.setOnClickListener(ItemClickHandler.INSTANCE);
         favorite.setOnFocusChangeListener(mFocusHandler);
         return favorite;
@@ -981,7 +1142,7 @@
      *
      * @param data The intent describing the shortcut.
      */
-    private void completeAddShortcut(Intent data, long container, long screenId, int cellX,
+    private void completeAddShortcut(Intent data, int container, int screenId, int cellX,
             int cellY, PendingRequestArgs args) {
         if (args.getRequestCode() != REQUEST_CREATE_SHORTCUT
                 || args.getPendingIntent().getComponent() == null) {
@@ -991,9 +1152,9 @@
         int[] cellXY = mTmpAddItemCellCoordinates;
         CellLayout layout = getCellLayout(container, screenId);
 
-        ShortcutInfo info = null;
+        WorkspaceItemInfo info = null;
         if (Utilities.ATLEAST_OREO) {
-            info = LauncherAppsCompatVO.createShortcutInfoFromPinItemRequest(
+            info = LauncherAppsCompatVO.createWorkspaceItemFromPinItemRequest(
                     this, LauncherAppsCompatVO.getPinItemRequest(data), 0);
         }
 
@@ -1057,13 +1218,8 @@
         }
     }
 
-    public FolderIcon findFolderIcon(final long folderIconId) {
-        return (FolderIcon) mWorkspace.getFirstMatch(new ItemOperator() {
-            @Override
-            public boolean evaluate(ItemInfo info, View view) {
-                return info != null && info.id == folderIconId;
-            }
-        });
+    public FolderIcon findFolderIcon(final int folderIconId) {
+        return (FolderIcon) mWorkspace.getHomescreenIconByItemId(folderIconId);
     }
 
     /**
@@ -1116,21 +1272,15 @@
         }
     };
 
-    public void updateIconBadges(final Set<PackageUserKey> updatedBadges) {
-        mWorkspace.updateIconBadges(updatedBadges);
-        mAppsView.getAppsStore().updateIconBadges(updatedBadges);
-
-        PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen(Launcher.this);
-        if (popup != null) {
-            popup.updateNotificationHeader(updatedBadges);
-        }
+    public void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
+        mWorkspace.updateNotificationDots(updatedDots);
+        mAppsView.getAppsStore().updateNotificationDots(updatedDots);
     }
 
     @Override
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
 
-        FirstFrameAnimatorHelper.initializeDrawListener(getWindow().getDecorView());
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onAttachedToWindow();
         }
@@ -1171,10 +1321,6 @@
         return mHotseat;
     }
 
-    public View getHotseatSearchBox() {
-        return mHotseatSearchBox;
-    }
-
     public <T extends View> T getOverviewPanel() {
         return (T) mOverviewPanel;
     }
@@ -1219,19 +1365,6 @@
 
         if (isActionMain) {
             if (!internalStateHandled) {
-                // Note: There should be at most one log per method call. This is enforced
-                // implicitly by using if-else statements.
-                UserEventDispatcher ued = getUserEventDispatcher();
-                AbstractFloatingView topOpenView = AbstractFloatingView.getTopOpenView(this);
-                if (topOpenView != null) {
-                    topOpenView.logActionCommand(Action.Command.HOME_INTENT);
-                } else if (alreadyOnHome) {
-                    Target target = newContainerTarget(mStateManager.getState().containerType);
-                    target.pageIndex = mWorkspace.getCurrentPage();
-                    ued.logActionCommand(Action.Command.HOME_INTENT, target,
-                            newContainerTarget(ContainerType.WORKSPACE));
-                }
-
                 // In all these cases, only animate if we're already on home
                 AbstractFloatingView.closeAllOpenViews(this, isStarted());
 
@@ -1246,11 +1379,18 @@
                     mAppsView.reset(isStarted() /* animate */);
                 }
 
-                if (shouldMoveToDefaultScreen && !mWorkspace.isTouchActive()) {
+                if (shouldMoveToDefaultScreen && !mWorkspace.isHandlingTouch()) {
                     mWorkspace.post(mWorkspace::moveToDefaultScreen);
                 }
             }
 
+            // 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));
+
             final View v = getWindow().peekDecorView();
             if (v != null && v.getWindowToken() != null) {
                 UiThreadHelper.hideKeyboardAsync(this, v.getWindowToken());
@@ -1296,6 +1436,8 @@
         if (mPendingRequestArgs != null) {
             outState.putParcelable(RUNTIME_STATE_PENDING_REQUEST_ARGS, mPendingRequestArgs);
         }
+        outState.putInt(RUNTIME_STATE_PENDING_REQUEST_CODE, mPendingActivityRequestCode);
+
         if (mPendingActivityResult != null) {
             outState.putParcelable(RUNTIME_STATE_PENDING_ACTIVITY_RESULT, mPendingActivityResult);
         }
@@ -1314,7 +1456,10 @@
         unregisterReceiver(mScreenOffReceiver);
         mWorkspace.removeFolderListeners();
 
-        UiFactory.setOnTouchControllersChangedListener(this, null);
+        if (mCancelTouchController != null) {
+            mCancelTouchController.run();
+            mCancelTouchController = null;
+        }
 
         // Stop callbacks from LauncherModel
         // It's possible to receive onDestroy after a new Launcher activity has
@@ -1332,11 +1477,8 @@
         }
 
         TextKeyListener.getInstance().release();
-
-        LauncherAnimUtils.onDestroyActivity();
-
         clearPendingBinds();
-
+        LauncherAppState.getIDP(this).removeOnChangeListener(this);
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onDestroy();
         }
@@ -1352,17 +1494,29 @@
 
     @Override
     public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
-        super.startActivityForResult(intent, requestCode, options);
+        if (requestCode != -1) {
+            mPendingActivityRequestCode = requestCode;
+        }
+        if (requestCode == -1
+                || !UiFactory.startActivityForResult(this, intent, requestCode, options)) {
+            super.startActivityForResult(intent, requestCode, options);
+        }
     }
 
     @Override
-    public void startIntentSenderForResult (IntentSender intent, int requestCode,
+    public void startIntentSenderForResult(IntentSender intent, int requestCode,
             Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options) {
-        try {
-            super.startIntentSenderForResult(intent, requestCode,
-                fillInIntent, flagsMask, flagsValues, extraFlags, options);
-        } catch (IntentSender.SendIntentException e) {
-            throw new ActivityNotFoundException();
+        if (requestCode != -1) {
+            mPendingActivityRequestCode = requestCode;
+        }
+        if (requestCode == -1 || !UiFactory.startIntentSenderForResult(this, intent, requestCode,
+                fillInIntent, flagsMask, flagsValues, extraFlags, options)) {
+            try {
+                super.startIntentSenderForResult(intent, requestCode,
+                        fillInIntent, flagsMask, flagsValues, extraFlags, options);
+            } catch (IntentSender.SendIntentException e) {
+                throw new ActivityNotFoundException();
+            }
         }
     }
 
@@ -1429,7 +1583,7 @@
         }
     }
 
-    public void addPendingItem(PendingAddItemInfo info, long container, long screenId,
+    public void addPendingItem(PendingAddItemInfo info, int container, int screenId,
             int[] cell, int spanX, int spanY) {
         info.container = container;
         info.screenId = screenId;
@@ -1505,7 +1659,7 @@
         }
     }
 
-    FolderIcon addFolder(CellLayout layout, long container, final long screenId, int cellX,
+    FolderIcon addFolder(CellLayout layout, int container, final int screenId, int cellX,
             int cellY) {
         final FolderInfo folderInfo = new FolderInfo();
         folderInfo.title = getText(R.string.folder_name);
@@ -1530,11 +1684,11 @@
      * @param deleteFromDb whether or not to delete this item from the db.
      */
     public boolean removeItem(View v, final ItemInfo itemInfo, boolean deleteFromDb) {
-        if (itemInfo instanceof ShortcutInfo) {
+        if (itemInfo instanceof WorkspaceItemInfo) {
             // Remove the shortcut from the folder before removing it from launcher
             View folderIcon = mWorkspace.getHomescreenIconByItemId(itemInfo.container);
             if (folderIcon instanceof FolderIcon) {
-                ((FolderInfo) folderIcon.getTag()).remove((ShortcutInfo) itemInfo, true);
+                ((FolderInfo) folderIcon.getTag()).remove((WorkspaceItemInfo) itemInfo, true);
             } else {
                 mWorkspace.removeWorkspaceItem(v);
             }
@@ -1554,7 +1708,7 @@
             final LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) itemInfo;
             mWorkspace.removeWorkspaceItem(v);
             if (deleteFromDb) {
-                deleteWidgetInfo(widgetInfo);
+                getModelWriter().deleteWidgetInfo(widgetInfo, getAppWidgetHost());
             }
         } else {
             return false;
@@ -1562,23 +1716,7 @@
         return true;
     }
 
-    /**
-     * Deletes the widget info and the widget id.
-     */
-    private void deleteWidgetInfo(final LauncherAppWidgetInfo widgetInfo) {
-        final LauncherAppWidgetHost appWidgetHost = getAppWidgetHost();
-        if (appWidgetHost != null && !widgetInfo.isCustomWidget() && widgetInfo.isWidgetIdAllocated()) {
-            // Deleting an app widget ID is a void call but writes to disk before returning
-            // to the caller...
-            new AsyncTask<Void, Void, Void>() {
-                public Void doInBackground(Void ... args) {
-                    appWidgetHost.deleteAppWidgetId(widgetInfo.appWidgetId);
-                    return null;
-                }
-            }.executeOnExecutor(Utilities.THREAD_POOL_EXECUTOR);
-        }
-        getModelWriter().deleteItemFromDatabase(widgetInfo);
-    }
+
 
     @Override
     public boolean dispatchKeyEvent(KeyEvent event) {
@@ -1605,14 +1743,8 @@
         AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this);
         if (topView != null && topView.onBackPressed()) {
             // Handled by the floating view.
-        } else if (!isInState(NORMAL)) {
-            LauncherState lastState = mStateManager.getLastState();
-            ued.logActionCommand(Action.Command.BACK, mStateManager.getState().containerType,
-                    lastState.containerType);
-            mStateManager.goToState(lastState);
         } else {
-            // Back button is a no-op here, but give at least some feedback for the button press
-            mWorkspace.showOutlinesTemporarily();
+            mStateManager.getState().onBackPressed(this);
         }
     }
 
@@ -1648,6 +1780,16 @@
     }
 
     @Override
+    public int getCurrentState() {
+        if(mStateManager.getState() == LauncherState.ALL_APPS) {
+            return StatsLogUtils.LAUNCHER_STATE_ALLAPPS;
+        } else if (mStateManager.getState() == OVERVIEW) {
+            return StatsLogUtils.LAUNCHER_STATE_OVERVIEW;
+        }
+        return StatsLogUtils.LAUNCHER_STATE_HOME;
+    }
+
+    @Override
     public void modifyUserEvent(LauncherLogProto.LauncherEvent event) {
         if (event.srcTarget != null && event.srcTarget.length > 0 &&
                 event.srcTarget[1].containerType == ContainerType.PREDICTION) {
@@ -1659,14 +1801,29 @@
             LauncherState state = mStateManager.getState();
             if (state == LauncherState.ALL_APPS) {
                 event.srcTarget[2].containerType = ContainerType.ALLAPPS;
-            } else if (state == LauncherState.OVERVIEW) {
+            } else if (state == OVERVIEW) {
                 event.srcTarget[2].containerType = ContainerType.TASKSWITCHER;
             }
         }
     }
 
-    public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
-        boolean success = super.startActivitySafely(v, intent, item);
+    public boolean startActivitySafely(View v, Intent intent, ItemInfo item,
+            @Nullable String sourceContainer) {
+        if (TestProtocol.sDebugTracing) {
+            android.util.Log.d(TestProtocol.NO_START_TAG,
+                    "startActivitySafely outer");
+        }
+
+        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));
+            UiFactory.clearSwipeSharedState(true /* finishAnimation */);
+            return true;
+        }
+
+        boolean success = super.startActivitySafely(v, intent, item, sourceContainer);
         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
@@ -1674,30 +1831,22 @@
             // state when we return to launcher.
             BubbleTextView btv = (BubbleTextView) v;
             btv.setStayPressed(true);
-            setOnResumeCallback(btv);
+            addOnResumeCallback(btv);
         }
         return success;
     }
 
     boolean isHotseatLayout(View layout) {
         // TODO: Remove this method
-        return mHotseat != null && layout != null &&
-                (layout instanceof CellLayout) && (layout == mHotseat.getLayout());
+        return mHotseat != null && (layout == mHotseat);
     }
 
     /**
      * Returns the CellLayout of the specified container at the specified screen.
      */
-    public CellLayout getCellLayout(long container, long screenId) {
-        if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
-            if (mHotseat != null) {
-                return mHotseat.getLayout();
-            } else {
-                return null;
-            }
-        } else {
-            return mWorkspace.getScreenWithId(screenId);
-        }
+    public CellLayout getCellLayout(int container, int screenId) {
+        return (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT)
+                ? mHotseat : mWorkspace.getScreenWithId(screenId);
     }
 
     @Override
@@ -1730,11 +1879,8 @@
         return result;
     }
 
-    public void setOnResumeCallback(OnResumeCallback callback) {
-        if (mOnResumeCallback != null) {
-            mOnResumeCallback.onLauncherResume();
-        }
-        mOnResumeCallback = callback;
+    public void addOnResumeCallback(OnResumeCallback callback) {
+        mOnResumeCallbacks.add(callback);
     }
 
     /**
@@ -1777,24 +1923,25 @@
         setWorkspaceLoading(true);
 
         // Clear the workspace because it's going to be rebound
+        mDragController.cancelDrag();
+
         mWorkspace.clearDropTargets();
         mWorkspace.removeAllWorkspaceScreens();
         mAppWidgetHost.clearViews();
 
         if (mHotseat != null) {
-            mHotseat.resetLayout(mDeviceProfile.isVerticalBarLayout());
+            mHotseat.resetLayout(getWallpaperDeviceProfile().isVerticalBarLayout());
         }
         TraceHelper.endSection("startBinding");
     }
 
     @Override
-    public void bindScreens(ArrayList<Long> orderedScreenIds) {
+    public void bindScreens(IntArray orderedScreenIds) {
         // Make sure the first screen is always at the start.
         if (FeatureFlags.QSB_ON_FIRST_SCREEN &&
                 orderedScreenIds.indexOf(Workspace.FIRST_SCREEN_ID) != 0) {
-            orderedScreenIds.remove(Workspace.FIRST_SCREEN_ID);
+            orderedScreenIds.removeValue(Workspace.FIRST_SCREEN_ID);
             orderedScreenIds.add(0, Workspace.FIRST_SCREEN_ID);
-            LauncherModel.updateWorkspaceScreenOrder(this, orderedScreenIds);
         } else if (!FeatureFlags.QSB_ON_FIRST_SCREEN && orderedScreenIds.isEmpty()) {
             // If there are no screens, we need to have an empty screen
             mWorkspace.addExtraEmptyScreen();
@@ -1807,10 +1954,10 @@
         mWorkspace.unlockWallpaperFromDefaultPageOnNextLayout();
     }
 
-    private void bindAddScreens(ArrayList<Long> orderedScreenIds) {
+    private void bindAddScreens(IntArray orderedScreenIds) {
         int count = orderedScreenIds.size();
         for (int i = 0; i < count; i++) {
-            long screenId = orderedScreenIds.get(i);
+            int screenId = orderedScreenIds.get(i);
             if (!FeatureFlags.QSB_ON_FIRST_SCREEN || screenId != Workspace.FIRST_SCREEN_ID) {
                 // No need to bind the first screen, as its always bound.
                 mWorkspace.insertNewWorkspaceScreenBeforeEmptyScreen(screenId);
@@ -1819,7 +1966,18 @@
     }
 
     @Override
-    public void bindAppsAdded(ArrayList<Long> newScreens, ArrayList<ItemInfo> addNotAnimated,
+    public void preAddApps() {
+        // If there's an undo snackbar, force it to complete to ensure empty screens are removed
+        // before trying to add new items.
+        mModelWriter.commitDelete();
+        AbstractFloatingView snackbar = AbstractFloatingView.getOpenView(this, TYPE_SNACKBAR);
+        if (snackbar != null) {
+            snackbar.post(() -> snackbar.close(true));
+        }
+    }
+
+    @Override
+    public void bindAppsAdded(IntArray newScreens, ArrayList<ItemInfo> addNotAnimated,
             ArrayList<ItemInfo> addAnimated) {
         // Add the new screens
         if (newScreens != null) {
@@ -1847,11 +2005,10 @@
     @Override
     public void bindItems(final List<ItemInfo> items, final boolean forceAnimateIcons) {
         // Get the list of added items and intersect them with the set of items here
-        final AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
         final Collection<Animator> bounceAnims = new ArrayList<>();
         final boolean animateIcons = forceAnimateIcons && canRunNewAppsAnimation();
         Workspace workspace = mWorkspace;
-        long newItemsScreenId = -1;
+        int newItemsScreenId = -1;
         int end = items.size();
         for (int i = 0; i < end; i++) {
             final ItemInfo item = items.get(i);
@@ -1867,7 +2024,7 @@
                 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
                 case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: {
-                    ShortcutInfo info = (ShortcutInfo) item;
+                    WorkspaceItemInfo info = (WorkspaceItemInfo) item;
                     view = createShortcut(info);
                     break;
                 }
@@ -1919,34 +2076,31 @@
             }
         }
 
-        if (animateIcons) {
-            // Animate to the correct page
-            if (newItemsScreenId > -1) {
-                long currentScreenId = mWorkspace.getScreenIdForPageIndex(mWorkspace.getNextPage());
-                final int newScreenIndex = mWorkspace.getPageIndexForScreenId(newItemsScreenId);
-                final Runnable startBounceAnimRunnable = new Runnable() {
-                    public void run() {
-                        anim.playTogether(bounceAnims);
-                        anim.start();
-                    }
-                };
-                if (newItemsScreenId != currentScreenId) {
-                    // We post the animation slightly delayed to prevent slowdowns
-                    // when we are loading right after we return to launcher.
-                    mWorkspace.postDelayed(new Runnable() {
-                        public void run() {
-                            if (mWorkspace != null) {
-                                AbstractFloatingView.closeAllOpenViews(Launcher.this, false);
+        // Animate to the correct page
+        if (animateIcons && newItemsScreenId > -1) {
+            AnimatorSet anim = new AnimatorSet();
+            anim.playTogether(bounceAnims);
 
-                                mWorkspace.snapToPage(newScreenIndex);
-                                mWorkspace.postDelayed(startBounceAnimRunnable,
-                                        NEW_APPS_ANIMATION_DELAY);
-                            }
+            int currentScreenId = mWorkspace.getScreenIdForPageIndex(mWorkspace.getNextPage());
+            final int newScreenIndex = mWorkspace.getPageIndexForScreenId(newItemsScreenId);
+            final Runnable startBounceAnimRunnable = anim::start;
+
+            if (newItemsScreenId != currentScreenId) {
+                // We post the animation slightly delayed to prevent slowdowns
+                // when we are loading right after we return to launcher.
+                mWorkspace.postDelayed(new Runnable() {
+                    public void run() {
+                        if (mWorkspace != null) {
+                            AbstractFloatingView.closeAllOpenViews(Launcher.this, false);
+
+                            mWorkspace.snapToPage(newScreenIndex);
+                            mWorkspace.postDelayed(startBounceAnimRunnable,
+                                    NEW_APPS_ANIMATION_DELAY);
                         }
-                    }, NEW_APPS_PAGE_MOVE_DELAY);
-                } else {
-                    mWorkspace.postDelayed(startBounceAnimRunnable, NEW_APPS_ANIMATION_DELAY);
-                }
+                    }
+                }, NEW_APPS_PAGE_MOVE_DELAY);
+            } else {
+                mWorkspace.postDelayed(startBounceAnimRunnable, NEW_APPS_ANIMATION_DELAY);
             }
         }
         workspace.requestLayout();
@@ -2055,7 +2209,7 @@
             // Verify that we own the widget
             if (appWidgetInfo == null) {
                 FileLog.e(TAG, "Removing invalid widget: id=" + item.appWidgetId);
-                deleteWidgetInfo(item);
+                getModelWriter().deleteWidgetInfo(item, getAppWidgetHost());
                 return null;
             }
 
@@ -2108,8 +2262,9 @@
         }
         mPendingExecutor = executor;
         if (!isInState(ALL_APPS)) {
-            mAppsView.getAppsStore().setDeferUpdates(true);
-            mPendingExecutor.execute(() -> mAppsView.getAppsStore().setDeferUpdates(false));
+            mAppsView.getAppsStore().enableDeferUpdates(AllAppsStore.DEFER_UPDATES_NEXT_DRAW);
+            mPendingExecutor.execute(() -> mAppsView.getAppsStore().disableDeferUpdates(
+                    AllAppsStore.DEFER_UPDATES_NEXT_DRAW));
         }
 
         executor.attachTo(this);
@@ -2145,7 +2300,7 @@
      *
      * Implementation of the method from LauncherModel.Callbacks.
      */
-    public void finishBindingItems() {
+    public void finishBindingItems(int pageBoundFirst) {
         TraceHelper.beginSection("finishBindingItems");
         mWorkspace.restoreInstanceStateForRemainingPages();
 
@@ -2160,6 +2315,11 @@
         InstallShortcutReceiver.disableAndFlushInstallQueue(
                 InstallShortcutReceiver.FLAG_LOADER_RUNNING, this);
 
+        // When undoing the removal of the last item on a page, return to that page.
+        // Since we are just resetting the current page without user interaction,
+        // override the previous page so we don't log the page switch.
+        mWorkspace.setCurrentPage(pageBoundFirst, pageBoundFirst /* overridePrevPage */);
+
         TraceHelper.endSection("finishBindingItems");
     }
 
@@ -2169,8 +2329,8 @@
     }
 
     private ValueAnimator createNewAppBounceAnimation(View v, int i) {
-        ValueAnimator bounceAnim = LauncherAnimUtils.ofViewAlphaAndScale(v, 1, 1, 1);
-        bounceAnim.setDuration(InstallShortcutReceiver.NEW_SHORTCUT_BOUNCE_DURATION);
+        ValueAnimator bounceAnim = new PropertyListBuilder().alpha(1).scale(1).build(v)
+                .setDuration(InstallShortcutReceiver.NEW_SHORTCUT_BOUNCE_DURATION);
         bounceAnim.setStartDelay(i * InstallShortcutReceiver.NEW_SHORTCUT_STAGGER_DELAY);
         bounceAnim.setInterpolator(new OvershootInterpolator(BOUNCE_ANIMATION_TENSION));
         return bounceAnim;
@@ -2183,18 +2343,14 @@
      */
     public void bindAllApplications(ArrayList<AppInfo> apps) {
         mAppsView.getAppsStore().setApps(apps);
-
-        if (mLauncherCallbacks != null) {
-            mLauncherCallbacks.bindAllApplications(apps);
-        }
     }
 
     /**
-     * Copies LauncherModel's map of activities to shortcut ids to Launcher's. This is necessary
+     * Copies LauncherModel's map of activities to shortcut counts to Launcher's. This is necessary
      * because LauncherModel's map is updated in the background, while Launcher runs on the UI.
      */
     @Override
-    public void bindDeepShortcutMap(MultiHashMap<ComponentKey, String> deepShortcutMapCopy) {
+    public void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMapCopy) {
         mPopupDataProvider.setDeepShortcutMap(deepShortcutMapCopy);
     }
 
@@ -2225,7 +2381,7 @@
      * @param updated list of shortcuts which have changed.
      */
     @Override
-    public void bindShortcutsChanged(ArrayList<ShortcutInfo> updated, final UserHandle user) {
+    public void bindWorkspaceItemsChanged(ArrayList<WorkspaceItemInfo> updated) {
         if (!updated.isEmpty()) {
             mWorkspace.updateShortcuts(updated);
         }
@@ -2262,10 +2418,6 @@
     @Override
     public void bindAllWidgets(final ArrayList<WidgetListRowEntry> allWidgets) {
         mPopupDataProvider.setAllWidgets(allWidgets);
-        AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this);
-        if (topView != null) {
-            topView.onWidgetsBound();
-        }
     }
 
     /**
@@ -2298,7 +2450,7 @@
             }
 
             writer.println(prefix + "  Hotseat");
-            ViewGroup layout = mHotseat.getLayout().getShortcutsAndWidgets();
+            ViewGroup layout = mHotseat.getShortcutsAndWidgets();
             for (int j = 0; j < layout.getChildCount(); j++) {
                 Object tag = layout.getChildAt(j).getTag();
                 if (tag != null) {
@@ -2312,6 +2464,9 @@
         writer.print(" mPendingRequestArgs=" + mPendingRequestArgs);
         writer.println(" mPendingActivityResult=" + mPendingActivityResult);
         writer.println(" mRotationHelper: " + mRotationHelper);
+        // Extra logging for b/116853349
+        mDragLayer.dump(prefix, writer);
+        mStateManager.dump(prefix, writer);
         dumpMisc(writer);
 
         try {
@@ -2328,7 +2483,6 @@
     }
 
     @Override
-    @TargetApi(Build.VERSION_CODES.N)
     public void onProvideKeyboardShortcuts(
             List<KeyboardShortcutGroup> data, Menu menu, int deviceId) {
 
@@ -2417,10 +2571,14 @@
     }
 
     public static Launcher getLauncher(Context context) {
-        if (context instanceof Launcher) {
-            return (Launcher) context;
-        }
-        return ((Launcher) ((ContextWrapper) context).getBaseContext());
+        return (Launcher) fromContext(context);
+    }
+
+    /**
+     * Just a wrapper around the type cast to allow easier tracking of calls.
+     */
+    public static <T extends Launcher> T cast(ActivityContext activityContext) {
+        return (T) activityContext;
     }
 
     /**
diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java
index 03ffded..74362ed 100644
--- a/src/com/android/launcher3/LauncherAnimUtils.java
+++ b/src/com/android/launcher3/LauncherAnimUtils.java
@@ -16,18 +16,11 @@
 
 package com.android.launcher3;
 
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.animation.ValueAnimator;
 import android.graphics.drawable.Drawable;
+import android.util.FloatProperty;
 import android.util.Property;
 import android.view.View;
-import android.view.ViewTreeObserver;
-
-import java.util.HashSet;
-import java.util.WeakHashMap;
+import android.view.ViewGroup.LayoutParams;
 
 public class LauncherAnimUtils {
     /**
@@ -42,105 +35,6 @@
     // 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;
 
-    static WeakHashMap<Animator, Object> sAnimators = new WeakHashMap<Animator, Object>();
-    static Animator.AnimatorListener sEndAnimListener = new Animator.AnimatorListener() {
-        public void onAnimationStart(Animator animation) {
-            sAnimators.put(animation, null);
-        }
-
-        public void onAnimationRepeat(Animator animation) {
-        }
-
-        public void onAnimationEnd(Animator animation) {
-            sAnimators.remove(animation);
-        }
-
-        public void onAnimationCancel(Animator animation) {
-            sAnimators.remove(animation);
-        }
-    };
-
-    public static void cancelOnDestroyActivity(Animator a) {
-        a.addListener(sEndAnimListener);
-    }
-
-    // Helper method. Assumes a draw is pending, and that if the animation's duration is 0
-    // it should be cancelled
-    public static void startAnimationAfterNextDraw(final Animator animator, final View view) {
-        view.getViewTreeObserver().addOnDrawListener(new ViewTreeObserver.OnDrawListener() {
-            private boolean mStarted = false;
-
-            public void onDraw() {
-                if (mStarted) return;
-                mStarted = true;
-                // Use this as a signal that the animation was cancelled
-                if (animator.getDuration() == 0) {
-                    return;
-                }
-                animator.start();
-
-                final ViewTreeObserver.OnDrawListener listener = this;
-                view.post(new Runnable() {
-                    public void run() {
-                        view.getViewTreeObserver().removeOnDrawListener(listener);
-                    }
-                });
-            }
-        });
-    }
-
-    public static void onDestroyActivity() {
-        HashSet<Animator> animators = new HashSet<Animator>(sAnimators.keySet());
-        for (Animator a : animators) {
-            if (a.isRunning()) {
-                a.cancel();
-            }
-            sAnimators.remove(a);
-        }
-    }
-
-    public static AnimatorSet createAnimatorSet() {
-        AnimatorSet anim = new AnimatorSet();
-        cancelOnDestroyActivity(anim);
-        return anim;
-    }
-
-    public static ValueAnimator ofFloat(float... values) {
-        ValueAnimator anim = new ValueAnimator();
-        anim.setFloatValues(values);
-        cancelOnDestroyActivity(anim);
-        return anim;
-    }
-
-    public static ObjectAnimator ofFloat(View target, Property<View, Float> property,
-            float... values) {
-        ObjectAnimator anim = ObjectAnimator.ofFloat(target, property, values);
-        cancelOnDestroyActivity(anim);
-        new FirstFrameAnimatorHelper(anim, target);
-        return anim;
-    }
-
-    public static ObjectAnimator ofViewAlphaAndScale(View target,
-            float alpha, float scaleX, float scaleY) {
-        return ofPropertyValuesHolder(target,
-                PropertyValuesHolder.ofFloat(View.ALPHA, alpha),
-                PropertyValuesHolder.ofFloat(View.SCALE_X, scaleX),
-                PropertyValuesHolder.ofFloat(View.SCALE_Y, scaleY));
-    }
-
-    public static ObjectAnimator ofPropertyValuesHolder(View target,
-            PropertyValuesHolder... values) {
-        return ofPropertyValuesHolder(target, target, values);
-    }
-
-    public static ObjectAnimator ofPropertyValuesHolder(Object target,
-            View view, PropertyValuesHolder... values) {
-        ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(target, values);
-        cancelOnDestroyActivity(anim);
-        new FirstFrameAnimatorHelper(anim, view);
-        return anim;
-    }
-
     public static final Property<Drawable, Integer> DRAWABLE_ALPHA =
             new Property<Drawable, Integer>(Integer.TYPE, "drawableAlpha") {
                 @Override
@@ -154,15 +48,15 @@
                 }
             };
 
-    public static final Property<View, Float> SCALE_PROPERTY =
-            new Property<View, Float>(Float.class, "scale") {
+    public static final FloatProperty<View> SCALE_PROPERTY =
+            new FloatProperty<View>("scale") {
                 @Override
                 public Float get(View view) {
                     return view.getScaleX();
                 }
 
                 @Override
-                public void set(View view, Float scale) {
+                public void setValue(View view, float scale) {
                     view.setScaleX(scale);
                     view.setScaleY(scale);
                 }
@@ -172,4 +66,58 @@
     public static int blockedFlingDurationFactor(float velocity) {
         return (int) Utilities.boundToRange(Math.abs(velocity) / 2, 2f, 6f);
     }
+
+    public static final Property<LayoutParams, Integer> LAYOUT_WIDTH =
+            new Property<LayoutParams, Integer>(Integer.TYPE, "width") {
+                @Override
+                public Integer get(LayoutParams lp) {
+                    return lp.width;
+                }
+
+                @Override
+                public void set(LayoutParams lp, Integer width) {
+                    lp.width = width;
+                }
+            };
+
+    public static final Property<LayoutParams, Integer> LAYOUT_HEIGHT =
+            new Property<LayoutParams, Integer>(Integer.TYPE, "height") {
+                @Override
+                public Integer get(LayoutParams lp) {
+                    return lp.height;
+                }
+
+                @Override
+                public void set(LayoutParams lp, Integer height) {
+                    lp.height = height;
+                }
+            };
+
+    public static final FloatProperty<View> VIEW_TRANSLATE_X =
+            View.TRANSLATION_X instanceof FloatProperty ? (FloatProperty) View.TRANSLATION_X
+                    : new FloatProperty<View>("translateX") {
+                        @Override
+                        public void setValue(View view, float v) {
+                            view.setTranslationX(v);
+                        }
+
+                        @Override
+                        public Float get(View view) {
+                            return view.getTranslationX();
+                        }
+                    };
+
+    public static final FloatProperty<View> VIEW_TRANSLATE_Y =
+            View.TRANSLATION_Y instanceof FloatProperty ? (FloatProperty) View.TRANSLATION_Y
+                    : new FloatProperty<View>("translateY") {
+                        @Override
+                        public void setValue(View view, float v) {
+                            view.setTranslationY(v);
+                        }
+
+                        @Override
+                        public Float get(View view) {
+                            return view.getTranslationY();
+                        }
+                    };
 }
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index a46692b..d07638a 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -16,64 +16,51 @@
 
 package com.android.launcher3;
 
+import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS;
+import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver;
+
+import android.app.KeyguardManager;
 import android.content.ComponentName;
 import android.content.ContentProviderClient;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.os.Looper;
+import android.os.Handler;
+import android.os.Process;
 import android.util.Log;
 
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.PackageInstallerCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.notification.NotificationListener;
-import com.android.launcher3.util.ConfigMonitor;
+import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.Preconditions;
-import com.android.launcher3.util.SettingsObserver;
-
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-
-import static com.android.launcher3.SettingsActivity.NOTIFICATION_BADGING;
+import com.android.launcher3.util.SecureSettingsObserver;
 
 public class LauncherAppState {
 
     public static final String ACTION_FORCE_ROLOAD = "force-reload-launcher";
 
     // We do not need any synchronization for this variable as its only written on UI thread.
-    private static LauncherAppState INSTANCE;
+    private static final MainThreadInitializedObject<LauncherAppState> INSTANCE =
+            new MainThreadInitializedObject<>((c) -> new LauncherAppState(c));
 
     private final Context mContext;
     private final LauncherModel mModel;
     private final IconCache mIconCache;
     private final WidgetPreviewLoader mWidgetCache;
     private final InvariantDeviceProfile mInvariantDeviceProfile;
-    private final SettingsObserver mNotificationBadgingObserver;
+    private final SecureSettingsObserver mNotificationDotsObserver;
 
     public static LauncherAppState getInstance(final Context context) {
-        if (INSTANCE == null) {
-            if (Looper.myLooper() == Looper.getMainLooper()) {
-                INSTANCE = new LauncherAppState(context.getApplicationContext());
-            } else {
-                try {
-                    return new MainThreadExecutor().submit(new Callable<LauncherAppState>() {
-                        @Override
-                        public LauncherAppState call() throws Exception {
-                            return LauncherAppState.getInstance(context);
-                        }
-                    }).get();
-                } catch (InterruptedException|ExecutionException e) {
-                    throw new RuntimeException(e);
-                }
-            }
-        }
-        return INSTANCE;
+        return INSTANCE.get(context);
     }
 
     public static LauncherAppState getInstanceNoCreate() {
-        return INSTANCE;
+        return INSTANCE.getNoCreate();
     }
 
     public Context getContext() {
@@ -89,7 +76,7 @@
         Preconditions.assertUIThread();
         mContext = context;
 
-        mInvariantDeviceProfile = new InvariantDeviceProfile(mContext);
+        mInvariantDeviceProfile = InvariantDeviceProfile.INSTANCE.get(mContext);
         mIconCache = new IconCache(mContext, mInvariantDeviceProfile);
         mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache);
         mModel = new LauncherModel(this, mIconCache, AppFilter.newInstance(mContext));
@@ -112,26 +99,41 @@
 
         mContext.registerReceiver(mModel, filter);
         UserManagerCompat.getInstance(mContext).enableAndResetCache();
-        new ConfigMonitor(mContext).register();
+        mInvariantDeviceProfile.addOnChangeListener(this::onIdpChanged);
+        new Handler().post( () -> mInvariantDeviceProfile.verifyConfigChangedInBackground(context));
 
-        if (!mContext.getResources().getBoolean(R.bool.notification_badging_enabled)) {
-            mNotificationBadgingObserver = null;
+        if (!mContext.getResources().getBoolean(R.bool.notification_dots_enabled)) {
+            mNotificationDotsObserver = null;
         } else {
-            // Register an observer to rebind the notification listener when badging is re-enabled.
-            mNotificationBadgingObserver = new SettingsObserver.Secure(
-                    mContext.getContentResolver()) {
-                @Override
-                public void onSettingChanged(boolean isNotificationBadgingEnabled) {
-                    if (isNotificationBadgingEnabled) {
-                        NotificationListener.requestRebind(new ComponentName(
-                                mContext, NotificationListener.class));
-                    }
-                }
-            };
-            mNotificationBadgingObserver.register(NOTIFICATION_BADGING);
+            // Register an observer to rebind the notification listener when dots are re-enabled.
+            mNotificationDotsObserver =
+                    newNotificationSettingsObserver(mContext, this::onNotificationSettingsChanged);
+            mNotificationDotsObserver.register();
+            mNotificationDotsObserver.dispatchOnChange();
         }
     }
 
+    protected 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();
+        }
+
+        mModel.forceReload();
+    }
+
     /**
      * Call from Application.onTerminate(), which is not guaranteed to ever be called.
      */
@@ -140,8 +142,8 @@
         final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mContext);
         launcherApps.removeOnAppsChangedCallback(mModel);
         PackageInstallerCompat.getInstance(mContext).onStop();
-        if (mNotificationBadgingObserver != null) {
-            mNotificationBadgingObserver.unregister();
+        if (mNotificationDotsObserver != null) {
+            mNotificationDotsObserver.unregister();
         }
     }
 
@@ -171,7 +173,7 @@
      * Shorthand for {@link #getInvariantDeviceProfile()}
      */
     public static InvariantDeviceProfile getIDP(Context context) {
-        return LauncherAppState.getInstance(context).getInvariantDeviceProfile();
+        return InvariantDeviceProfile.INSTANCE.get(context);
     }
 
     private static LauncherProvider getLocalProvider(Context context) {
diff --git a/src/com/android/launcher3/LauncherAppTransitionManager.java b/src/com/android/launcher3/LauncherAppTransitionManager.java
index 4037a23..c55c120 100644
--- a/src/com/android/launcher3/LauncherAppTransitionManager.java
+++ b/src/com/android/launcher3/LauncherAppTransitionManager.java
@@ -17,45 +17,54 @@
 package com.android.launcher3;
 
 
+import android.animation.Animator;
 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 {
+public class LauncherAppTransitionManager implements ResourceBasedOverride {
 
     public static LauncherAppTransitionManager newInstance(Context context) {
-        return Utilities.getOverrideObject(LauncherAppTransitionManager.class,
+        return Overrides.getObject(LauncherAppTransitionManager.class,
                 context, R.string.app_transition_manager_class);
     }
 
     public ActivityOptions getActivityLaunchOptions(Launcher launcher, View v) {
-        if (Utilities.ATLEAST_MARSHMALLOW) {
-            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();
-                }
+        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);
-        } else if (Utilities.ATLEAST_LOLLIPOP_MR1) {
-            // On L devices, we use the device default slide-up transition.
-            // On L MR1 devices, we use a custom version of the slide-up transition which
-            // doesn't have the delay present in the device default.
-            return ActivityOptions.makeCustomAnimation(launcher, R.anim.task_open_enter,
-                    R.anim.no_anim);
         }
-        return null;
+        return ActivityOptions.makeClipRevealAnimation(v, left, top, width, height);
+    }
+
+    public boolean supportsAdaptiveIconAnimation() {
+        return false;
+    }
+
+    /**
+     * Number of animations which run on state properties.
+     */
+    public int getStateElementAnimationsCount() {
+        return 0;
+    }
+
+    public Animator createStateElementAnimation(int index, float... values) {
+        throw new RuntimeException("Unknown gesture animation " + index);
     }
 }
diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java
index 56671a1..7f5ac52 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -26,7 +26,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.Handler;
-import android.util.Log;
 import android.util.SparseArray;
 import android.view.LayoutInflater;
 import android.widget.Toast;
@@ -135,9 +134,6 @@
      * @see #setResumed(boolean)
      */
     public void setListenIfResumed(boolean listenIfResumed) {
-        if (!Utilities.ATLEAST_NOUGAT_MR1) {
-            return;
-        }
         if (listenIfResumed == ((mFlags & FLAG_LISTEN_IF_RESUMED) != 0)) {
             return;
         }
diff --git a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
index 80758c9..228c07e 100644
--- a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
@@ -2,11 +2,15 @@
 
 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.os.Parcel;
+import android.os.UserHandle;
+
+import com.android.launcher3.icons.ComponentWithLabel;
 
 /**
  * This class is a thin wrapper around the framework AppWidgetProviderInfo class. This class affords
@@ -14,7 +18,8 @@
  * (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 {
+public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo
+        implements ComponentWithLabel {
 
     public static final String CLS_CUSTOM_WIDGET_PREFIX = "#custom-widget-";
 
@@ -53,19 +58,13 @@
     public void initSpans(Context context) {
         InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
 
-        Point paddingLand = idp.landscapeProfile.getTotalWorkspacePadding();
-        Point paddingPort = idp.portraitProfile.getTotalWorkspacePadding();
+        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 = DeviceProfile.calculateCellWidth(Math.min(
-                idp.landscapeProfile.widthPx - paddingLand.x,
-                idp.portraitProfile.widthPx - paddingPort.x),
-                idp.numColumns);
-        float smallestCellHeight = DeviceProfile.calculateCellWidth(Math.min(
-                idp.landscapeProfile.heightPx - paddingLand.y,
-                idp.portraitProfile.heightPx - paddingPort.y),
-                idp.numRows);
+        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.
@@ -102,4 +101,14 @@
             return 0;
         }
     }
- }
+
+    @Override
+    public final ComponentName getComponent() {
+        return provider;
+    }
+
+    @Override
+    public final UserHandle getUser() {
+        return getProfile();
+    }
+}
diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java
index 34bdb3c..dfe75ec 100644
--- a/src/com/android/launcher3/LauncherCallbacks.java
+++ b/src/com/android/launcher3/LauncherCallbacks.java
@@ -21,7 +21,6 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.util.ArrayList;
 
 /**
  * LauncherCallbacks is an interface used to extend the Launcher activity. It includes many hooks
@@ -53,20 +52,19 @@
     boolean handleBackPressed();
     void onTrimMemory(int level);
 
+    /**
+     * Called when the launcher state changed
+     */
+    default void onStateChanged() { }
+
     /*
      * Extension points for providing custom behavior on certain user interactions.
      */
     void onLauncherProviderChange();
-    void bindAllApplications(ArrayList<AppInfo> apps);
 
     /**
      * Starts a search with {@param initialQuery}. Return false if search was not started.
      */
     boolean startSearch(
             String initialQuery, boolean selectInitialQuery, Bundle appSearchData);
-
-    /*
-     * Extensions points for adding / replacing some other aspects of the Launcher experience.
-     */
-    boolean hasSettings();
 }
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 37538ae..d79f5d5 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -20,18 +20,14 @@
 import static com.android.launcher3.config.FeatureFlags.IS_DOGFOOD_BUILD;
 
 import android.content.BroadcastReceiver;
-import android.content.ContentProviderOperation;
-import android.content.ContentResolver;
-import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
-import android.net.Uri;
+import android.content.pm.ShortcutInfo;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Process;
 import android.os.UserHandle;
-import android.support.annotation.Nullable;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
@@ -39,7 +35,8 @@
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
 import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.graphics.LauncherIcons;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.model.AddWorkspaceItemsTask;
 import com.android.launcher3.model.BaseModelUpdateTask;
 import com.android.launcher3.model.BgDataModel;
@@ -51,15 +48,12 @@
 import com.android.launcher3.model.PackageUpdatedTask;
 import com.android.launcher3.model.ShortcutsChangedTask;
 import com.android.launcher3.model.UserLockStateChangedTask;
-import com.android.launcher3.provider.LauncherDbUtils;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.Preconditions;
-import com.android.launcher3.util.Provider;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.util.ViewOnDrawExecutor;
 import com.android.launcher3.widget.WidgetListRowEntry;
@@ -68,11 +62,14 @@
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.Executor;
+import java.util.function.Supplier;
+
+import androidx.annotation.Nullable;
 
 /**
  * Maintains in-memory state of the Launcher. It is expected that there should be only one
@@ -93,10 +90,12 @@
     @Thunk boolean mIsLoaderTaskRunning;
 
     @Thunk static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
+    private static final Looper mWorkerLooper;
     static {
         sWorkerThread.start();
+        mWorkerLooper = sWorkerThread.getLooper();
     }
-    @Thunk static final Handler sWorker = new Handler(sWorkerThread.getLooper());
+    @Thunk static final Handler sWorker = new Handler(mWorkerLooper);
 
     // Indicates whether the current model data is valid or not.
     // We start off with everything not loaded. After that, we assume that
@@ -141,16 +140,16 @@
         public void clearPendingBinds();
         public void startBinding();
         public void bindItems(List<ItemInfo> shortcuts, boolean forceAnimateIcons);
-        public void bindScreens(ArrayList<Long> orderedScreenIds);
+        public void bindScreens(IntArray orderedScreenIds);
         public void finishFirstPageBind(ViewOnDrawExecutor executor);
-        public void finishBindingItems();
+        public void finishBindingItems(int pageBoundFirst);
         public void bindAllApplications(ArrayList<AppInfo> apps);
         public void bindAppsAddedOrUpdated(ArrayList<AppInfo> apps);
-        public void bindAppsAdded(ArrayList<Long> newScreens,
-                                  ArrayList<ItemInfo> addNotAnimated,
-                                  ArrayList<ItemInfo> addAnimated);
+        public void preAddApps();
+        public void bindAppsAdded(IntArray newScreens,
+                ArrayList<ItemInfo> addNotAnimated, ArrayList<ItemInfo> addAnimated);
         public void bindPromiseAppProgressUpdated(PromiseAppInfo app);
-        public void bindShortcutsChanged(ArrayList<ShortcutInfo> updated, UserHandle user);
+        public void bindWorkspaceItemsChanged(ArrayList<WorkspaceItemInfo> updated);
         public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets);
         public void bindRestoreItemsChange(HashSet<ItemInfo> updates);
         public void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher);
@@ -158,7 +157,7 @@
         public void bindAllWidgets(ArrayList<WidgetListRowEntry> widgets);
         public void onPageBoundSynchronously(int page);
         public void executeOnNextDraw(ViewOnDrawExecutor executor);
-        public void bindDeepShortcutMap(MultiHashMap<ComponentKey, String> deepShortcutMap);
+        public void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMap);
     }
 
     LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
@@ -166,17 +165,6 @@
         mBgAllAppsList = new AllAppsList(iconCache, appFilter);
     }
 
-    /** Runs the specified runnable immediately if called from the worker thread, otherwise it is
-     * posted on the worker thread handler. */
-    private static void runOnWorkerThread(Runnable r) {
-        if (sWorkerThread.getThreadId() == Process.myTid()) {
-            r.run();
-        } else {
-            // If we are not on the worker thread, then post to the worker handler
-            sWorker.post(r);
-        }
-    }
-
     public void setPackageState(PackageInstallInfo installInfo) {
         enqueueModelUpdateTask(new PackageInstallStateChangedTask(installInfo));
     }
@@ -195,6 +183,10 @@
      * Adds the provided items to the workspace.
      */
     public void addAndBindAddedWorkspaceItems(List<Pair<ItemInfo, Object>> itemList) {
+        Callbacks callbacks = getCallback();
+        if (callbacks != null) {
+            callbacks.preAddApps();
+        }
         enqueueModelUpdateTask(new AddWorkspaceItemsTask(itemList));
     }
 
@@ -203,105 +195,6 @@
                 hasVerticalHotseat, verifyChanges);
     }
 
-    static void checkItemInfoLocked(
-            final long itemId, final ItemInfo item, StackTraceElement[] stackTrace) {
-        ItemInfo modelItem = sBgDataModel.itemsIdMap.get(itemId);
-        if (modelItem != null && item != modelItem) {
-            // check all the data is consistent
-            if (modelItem instanceof ShortcutInfo && item instanceof ShortcutInfo) {
-                ShortcutInfo modelShortcut = (ShortcutInfo) modelItem;
-                ShortcutInfo shortcut = (ShortcutInfo) item;
-                if (modelShortcut.title.toString().equals(shortcut.title.toString()) &&
-                        modelShortcut.intent.filterEquals(shortcut.intent) &&
-                        modelShortcut.id == shortcut.id &&
-                        modelShortcut.itemType == shortcut.itemType &&
-                        modelShortcut.container == shortcut.container &&
-                        modelShortcut.screenId == shortcut.screenId &&
-                        modelShortcut.cellX == shortcut.cellX &&
-                        modelShortcut.cellY == shortcut.cellY &&
-                        modelShortcut.spanX == shortcut.spanX &&
-                        modelShortcut.spanY == shortcut.spanY) {
-                    // For all intents and purposes, this is the same object
-                    return;
-                }
-            }
-
-            // the modelItem needs to match up perfectly with item if our model is
-            // to be consistent with the database-- for now, just require
-            // modelItem == item or the equality check above
-            String msg = "item: " + ((item != null) ? item.toString() : "null") +
-                    "modelItem: " +
-                    ((modelItem != null) ? modelItem.toString() : "null") +
-                    "Error: ItemInfo passed to checkItemInfo doesn't match original";
-            RuntimeException e = new RuntimeException(msg);
-            if (stackTrace != null) {
-                e.setStackTrace(stackTrace);
-            }
-            throw e;
-        }
-    }
-
-    static void checkItemInfo(final ItemInfo item) {
-        final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
-        final long itemId = item.id;
-        Runnable r = new Runnable() {
-            public void run() {
-                synchronized (sBgDataModel) {
-                    checkItemInfoLocked(itemId, item, stackTrace);
-                }
-            }
-        };
-        runOnWorkerThread(r);
-    }
-
-    /**
-     * Update the order of the workspace screens in the database. The array list contains
-     * a list of screen ids in the order that they should appear.
-     */
-    public static void updateWorkspaceScreenOrder(Context context, final ArrayList<Long> screens) {
-        final ArrayList<Long> screensCopy = new ArrayList<Long>(screens);
-        final ContentResolver cr = context.getContentResolver();
-        final Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
-
-        // Remove any negative screen ids -- these aren't persisted
-        Iterator<Long> iter = screensCopy.iterator();
-        while (iter.hasNext()) {
-            long id = iter.next();
-            if (id < 0) {
-                iter.remove();
-            }
-        }
-
-        Runnable r = new Runnable() {
-            @Override
-            public void run() {
-                ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
-                // Clear the table
-                ops.add(ContentProviderOperation.newDelete(uri).build());
-                int count = screensCopy.size();
-                for (int i = 0; i < count; i++) {
-                    ContentValues v = new ContentValues();
-                    long screenId = screensCopy.get(i);
-                    v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
-                    v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
-                    ops.add(ContentProviderOperation.newInsert(uri).withValues(v).build());
-                }
-
-                try {
-                    cr.applyBatch(LauncherProvider.AUTHORITY, ops);
-                } catch (Exception ex) {
-                    throw new RuntimeException(ex);
-                }
-
-                synchronized (sBgDataModel) {
-                    sBgDataModel.workspaceScreens.clear();
-                    sBgDataModel.workspaceScreens.addAll(screensCopy);
-                }
-            }
-        };
-        runOnWorkerThread(r);
-    }
-
     /**
      * Set this as the current Launcher activity object for the loader.
      */
@@ -363,12 +256,12 @@
     }
 
     @Override
-    public void onShortcutsChanged(String packageName, List<ShortcutInfoCompat> shortcuts,
+    public void onShortcutsChanged(String packageName, List<ShortcutInfo> shortcuts,
             UserHandle user) {
         enqueueModelUpdateTask(new ShortcutsChangedTask(packageName, shortcuts, user, true));
     }
 
-    public void updatePinnedShortcuts(String packageName, List<ShortcutInfoCompat> shortcuts,
+    public void updatePinnedShortcuts(String packageName, List<ShortcutInfo> shortcuts,
             UserHandle user) {
         enqueueModelUpdateTask(new ShortcutsChangedTask(packageName, shortcuts, user, false));
     }
@@ -408,15 +301,21 @@
                 }
             }
         } else if (IS_DOGFOOD_BUILD && ACTION_FORCE_ROLOAD.equals(action)) {
-            forceReload();
+            Launcher l = (Launcher) getCallback();
+            l.reload();
         }
     }
 
+    public void forceReload() {
+        forceReload(-1);
+    }
+
     /**
      * Reloads the workspace items from the DB and re-binds the workspace. This should generally
      * not be called as DB updates are automatically followed by UI update
+     * @param synchronousBindPage The page to bind first. Can pass -1 to use the current page.
      */
-    public void forceReload() {
+    public void forceReload(int synchronousBindPage) {
         synchronized (mLock) {
             // Stop any existing loaders first, so they don't set mModelLoaded to true later
             stopLoader();
@@ -427,7 +326,10 @@
         // the next time launcher starts
         Callbacks callbacks = getCallback();
         if (callbacks != null) {
-            startLoader(callbacks.getCurrentWorkspaceScreen());
+            if (synchronousBindPage < 0) {
+                synchronousBindPage = callbacks.getCurrentWorkspaceScreen();
+            }
+            startLoader(synchronousBindPage);
         }
     }
 
@@ -488,7 +390,10 @@
         synchronized (mLock) {
             stopLoader();
             mLoaderTask = new LoaderTask(mApp, mBgAllAppsList, sBgDataModel, results);
-            runOnWorkerThread(mLoaderTask);
+
+            // Always post the loader task, instead of running directly (even on same thread) so
+            // that we exit any nested synchronized blocks
+            sWorker.post(mLoaderTask);
         }
     }
 
@@ -501,18 +406,6 @@
         }
     }
 
-    /**
-     * Loads the workspace screen ids in an ordered list.
-     */
-    public static ArrayList<Long> loadWorkspaceScreensDb(Context context) {
-        final ContentResolver contentResolver = context.getContentResolver();
-        final Uri screensUri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
-
-        // Get screens ordered by rank.
-        return LauncherDbUtils.getScreenIdsFromCursor(contentResolver.query(
-                screensUri, null, null, null, LauncherSettings.WorkspaceScreens.SCREEN_RANK));
-    }
-
     public void onInstallSessionCreated(final PackageInstallInfo sessionInfo) {
         enqueueModelUpdateTask(new BaseModelUpdateTask() {
             @Override
@@ -576,10 +469,8 @@
      * use partial updates similar to {@link UserManagerCompat}
      */
     public void refreshShortcutsIfRequired() {
-        if (Utilities.ATLEAST_NOUGAT_MR1) {
-            sWorker.removeCallbacks(mShortcutPermissionCheckRunnable);
-            sWorker.post(mShortcutPermissionCheckRunnable);
-        }
+        sWorker.removeCallbacks(mShortcutPermissionCheckRunnable);
+        sWorker.post(mShortcutPermissionCheckRunnable);
     }
 
     /**
@@ -592,9 +483,28 @@
                 CacheDataUpdatedTask.OP_CACHE_UPDATE, user, updatedPackages));
     }
 
+    /**
+     * Called when the labels for the widgets has updated in the icon cache.
+     */
+    public void onWidgetLabelsUpdated(HashSet<String> updatedPackages, UserHandle user) {
+        enqueueModelUpdateTask(new BaseModelUpdateTask() {
+            @Override
+            public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+                dataModel.widgetsModel.onPackageIconsUpdated(updatedPackages, user, app);
+                bindUpdatedWidgets(dataModel);
+            }
+        });
+    }
+
     public void enqueueModelUpdateTask(ModelUpdateTask task) {
         task.init(mApp, this, sBgDataModel, mBgAllAppsList, mUiExecutor);
-        runOnWorkerThread(task);
+
+        if (sWorkerThread.getThreadId() == Process.myTid()) {
+            task.run();
+        } else {
+            // If we are not on the worker thread, then post to the worker handler
+            sWorker.post(task);
+        }
     }
 
     /**
@@ -619,31 +529,28 @@
 
     }
 
-    public void updateAndBindShortcutInfo(final ShortcutInfo si, final ShortcutInfoCompat info) {
-        updateAndBindShortcutInfo(new Provider<ShortcutInfo>() {
-            @Override
-            public ShortcutInfo get() {
-                si.updateFromDeepShortcutInfo(info, mApp.getContext());
-                LauncherIcons li = LauncherIcons.obtain(mApp.getContext());
-                li.createShortcutIcon(info).applyTo(si);
-                li.recycle();
-                return si;
-            }
+    public void updateAndBindWorkspaceItem(WorkspaceItemInfo si, ShortcutInfo info) {
+        updateAndBindWorkspaceItem(() -> {
+            si.updateFromDeepShortcutInfo(info, mApp.getContext());
+            LauncherIcons li = LauncherIcons.obtain(mApp.getContext());
+            si.applyFrom(li.createShortcutIcon(info));
+            li.recycle();
+            return si;
         });
     }
 
     /**
      * Utility method to update a shortcut on the background thread.
      */
-    public void updateAndBindShortcutInfo(final Provider<ShortcutInfo> shortcutProvider) {
+    public void updateAndBindWorkspaceItem(final Supplier<WorkspaceItemInfo> itemProvider) {
         enqueueModelUpdateTask(new BaseModelUpdateTask() {
             @Override
             public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
-                ShortcutInfo info = shortcutProvider.get();
+                WorkspaceItemInfo info = itemProvider.get();
                 getModelWriter().updateItemInDatabase(info);
-                ArrayList<ShortcutInfo> update = new ArrayList<>();
+                ArrayList<WorkspaceItemInfo> update = new ArrayList<>();
                 update.add(info);
-                bindUpdatedShortcuts(update, info.user);
+                bindUpdatedWorkspaceItems(update);
             }
         });
     }
@@ -677,7 +584,7 @@
      * @return the looper for the worker thread which can be used to start background tasks.
      */
     public static Looper getWorkerLooper() {
-        return sWorkerThread.getLooper();
+        return mWorkerLooper;
     }
 
     public static void setWorkerPriority(final int priority) {
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 7d208d4..6ad5c36 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -16,7 +16,11 @@
 
 package com.android.launcher3;
 
+import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
+import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
+
 import android.annotation.TargetApi;
+import android.app.backup.BackupManager;
 import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetManager;
 import android.content.ComponentName;
@@ -30,8 +34,10 @@
 import android.content.OperationApplicationException;
 import android.content.SharedPreferences;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ProviderInfo;
 import android.content.res.Resources;
 import android.database.Cursor;
+import android.database.DatabaseUtils;
 import android.database.SQLException;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteQueryBuilder;
@@ -45,12 +51,14 @@
 import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.provider.BaseColumns;
+import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Xml;
 
 import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback;
 import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.LauncherSettings.WorkspaceScreens;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.FileLog;
@@ -58,18 +66,25 @@
 import com.android.launcher3.provider.LauncherDbUtils;
 import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
 import com.android.launcher3.provider.RestoreDbTask;
+import com.android.launcher3.util.IOUtils;
+import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.NoLocaleSQLiteHelper;
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.Thunk;
 
+import org.xmlpull.v1.XmlPullParser;
+
 import java.io.File;
 import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
 import java.io.PrintWriter;
+import java.io.StringReader;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
+import java.util.Arrays;
+import java.util.Locale;
 
 public class LauncherProvider extends ContentProvider {
     private static final String TAG = "LauncherProvider";
@@ -79,15 +94,14 @@
 
     /**
      * 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 = 27;
+    public static final int SCHEMA_VERSION = 28;
 
-    public static final String AUTHORITY = FeatureFlags.AUTHORITY;
+    public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".settings";
 
     static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED";
 
-    private static final String RESTRICTION_PACKAGE_NAME = "workspace.configuration.package.name";
-
     private final ChangeListenerWrapper mListenerWrapper = new ChangeListenerWrapper();
     private Handler mListenerHandler;
 
@@ -144,7 +158,8 @@
             mOpenHelper = new DatabaseHelper(getContext(), mListenerHandler);
 
             if (RestoreDbTask.isPending(getContext())) {
-                if (!RestoreDbTask.performRestore(mOpenHelper)) {
+                if (!RestoreDbTask.performRestore(getContext(), mOpenHelper,
+                        new BackupManager(getContext()))) {
                     mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
                 }
                 // Set is pending to false irrespective of the result, so that it doesn't get
@@ -170,20 +185,20 @@
         return result;
     }
 
-    @Thunk static long dbInsertAndCheck(DatabaseHelper helper,
+    @Thunk static int dbInsertAndCheck(DatabaseHelper helper,
             SQLiteDatabase db, String table, String nullColumnHack, ContentValues values) {
         if (values == null) {
             throw new RuntimeException("Error: attempting to insert null values");
         }
-        if (!values.containsKey(LauncherSettings.ChangeLogColumns._ID)) {
+        if (!values.containsKey(LauncherSettings.Favorites._ID)) {
             throw new RuntimeException("Error: attempting to add item without specifying an id");
         }
-        helper.checkId(table, values);
-        return db.insert(table, nullColumnHack, values);
+        helper.checkId(values);
+        return (int) db.insert(table, nullColumnHack, values);
     }
 
     private void reloadLauncherIfExternal() {
-        if (Utilities.ATLEAST_MARSHMALLOW && Binder.getCallingPid() != Process.myPid()) {
+        if (Binder.getCallingPid() != Process.myPid()) {
             LauncherAppState app = LauncherAppState.getInstanceNoCreate();
             if (app != null) {
                 app.getModel().forceReload();
@@ -205,32 +220,19 @@
 
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
         addModifiedTime(initialValues);
-        final long rowId = dbInsertAndCheck(mOpenHelper, db, args.table, null, initialValues);
+        final int rowId = dbInsertAndCheck(mOpenHelper, db, args.table, null, initialValues);
         if (rowId < 0) return null;
+        mOpenHelper.onAddOrDeleteOp(db);
 
         uri = ContentUris.withAppendedId(uri, rowId);
         notifyListeners();
-
-        if (Utilities.ATLEAST_MARSHMALLOW) {
-            reloadLauncherIfExternal();
-        } else {
-            // Deprecated behavior to support legacy devices which rely on provider callbacks.
-            LauncherAppState app = LauncherAppState.getInstanceNoCreate();
-            if (app != null && "true".equals(uri.getQueryParameter("isExternalAdd"))) {
-                app.getModel().forceReload();
-            }
-
-            String notify = uri.getQueryParameter("notify");
-            if (notify == null || "true".equals(notify)) {
-                getContext().getContentResolver().notifyChange(uri, null);
-            }
-        }
+        reloadLauncherIfExternal();
         return uri;
     }
 
     private boolean initializeExternalAdd(ContentValues values) {
         // 1. Ensure that externally added items have a valid item id
-        long id = mOpenHelper.generateNewItemId();
+        int id = mOpenHelper.generateNewItemId();
         values.put(LauncherSettings.Favorites._ID, id);
 
         // 2. In the case of an app widget, and if no app widget id is specified, we
@@ -262,24 +264,7 @@
             }
         }
 
-        // Add screen id if not present
-        long screenId = values.getAsLong(LauncherSettings.Favorites.SCREEN);
-        SQLiteStatement stmp = null;
-        try {
-            stmp = mOpenHelper.getWritableDatabase().compileStatement(
-                    "INSERT OR IGNORE INTO workspaceScreens (_id, screenRank) " +
-                            "select ?, (ifnull(MAX(screenRank), -1)+1) from workspaceScreens");
-            stmp.bindLong(1, screenId);
-
-            ContentValues valuesInserted = new ContentValues();
-            valuesInserted.put(LauncherSettings.BaseLauncherColumns._ID, stmp.executeInsert());
-            mOpenHelper.checkId(WorkspaceScreens.TABLE_NAME, valuesInserted);
-            return true;
-        } catch (Exception e) {
-            return false;
-        } finally {
-            Utilities.closeSilently(stmp);
-        }
+        return true;
     }
 
     @Override
@@ -296,6 +281,7 @@
                     return 0;
                 }
             }
+            mOpenHelper.onAddOrDeleteOp(db);
             t.commit();
         }
 
@@ -304,15 +290,30 @@
         return values.length;
     }
 
+    @TargetApi(Build.VERSION_CODES.M)
     @Override
     public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
             throws OperationApplicationException {
         createDbIfNotExists();
         try (SQLiteTransaction t = new SQLiteTransaction(mOpenHelper.getWritableDatabase())) {
-            ContentProviderResult[] result =  super.applyBatch(operations);
+            boolean isAddOrDelete = false;
+
+            final int numOperations = operations.size();
+            final ContentProviderResult[] results = new ContentProviderResult[numOperations];
+            for (int i = 0; i < numOperations; i++) {
+                ContentProviderOperation op = operations.get(i);
+                results[i] = op.apply(this, results, i);
+
+                isAddOrDelete |= (op.isInsert() || op.isDelete()) &&
+                        results[i].count != null && results[i].count > 0;
+            }
+            if (isAddOrDelete) {
+                mOpenHelper.onAddOrDeleteOp(t.getDb());
+            }
+
             t.commit();
             reloadLauncherIfExternal();
-            return result;
+            return results;
         }
     }
 
@@ -329,6 +330,7 @@
         }
         int count = db.delete(args.table, args.where, args.args);
         if (count > 0) {
+            mOpenHelper.onAddOrDeleteOp(db);
             notifyListeners();
             reloadLauncherIfExternal();
         }
@@ -369,17 +371,18 @@
             }
             case LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS: {
                 Bundle result = new Bundle();
-                result.putSerializable(LauncherSettings.Settings.EXTRA_VALUE, deleteEmptyFolders());
+                result.putIntArray(LauncherSettings.Settings.EXTRA_VALUE, deleteEmptyFolders()
+                        .toArray());
                 return result;
             }
             case LauncherSettings.Settings.METHOD_NEW_ITEM_ID: {
                 Bundle result = new Bundle();
-                result.putLong(LauncherSettings.Settings.EXTRA_VALUE, mOpenHelper.generateNewItemId());
+                result.putInt(LauncherSettings.Settings.EXTRA_VALUE, mOpenHelper.generateNewItemId());
                 return result;
             }
             case LauncherSettings.Settings.METHOD_NEW_SCREEN_ID: {
                 Bundle result = new Bundle();
-                result.putLong(LauncherSettings.Settings.EXTRA_VALUE, mOpenHelper.generateNewScreenId());
+                result.putInt(LauncherSettings.Settings.EXTRA_VALUE, mOpenHelper.generateNewScreenId());
                 return result;
             }
             case LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB: {
@@ -394,6 +397,17 @@
                 mOpenHelper.removeGhostWidgets(mOpenHelper.getWritableDatabase());
                 return null;
             }
+            case LauncherSettings.Settings.METHOD_NEW_TRANSACTION: {
+                Bundle result = new Bundle();
+                result.putBinder(LauncherSettings.Settings.EXTRA_VALUE,
+                        new SQLiteTransaction(mOpenHelper.getWritableDatabase()));
+                return result;
+            }
+            case LauncherSettings.Settings.METHOD_REFRESH_BACKUP_TABLE: {
+                mOpenHelper.mBackupTableExists =
+                        tableExists(mOpenHelper.getReadableDatabase(), Favorites.BACKUP_TABLE_NAME);
+                return null;
+            }
         }
         return null;
     }
@@ -402,8 +416,7 @@
      * Deletes any empty folder from the DB.
      * @return Ids of deleted folders.
      */
-    private ArrayList<Long> deleteEmptyFolders() {
-        ArrayList<Long> folderIds = new ArrayList<>();
+    private IntArray deleteEmptyFolders() {
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
         try (SQLiteTransaction t = new SQLiteTransaction(db)) {
             // Select folders whose id do not match any container value.
@@ -412,21 +425,19 @@
                     + LauncherSettings.Favorites._ID +  " NOT IN (SELECT " +
                             LauncherSettings.Favorites.CONTAINER + " FROM "
                                 + Favorites.TABLE_NAME + ")";
-            try (Cursor c = db.query(Favorites.TABLE_NAME,
-                    new String[] {LauncherSettings.Favorites._ID},
-                    selection, null, null, null, null)) {
-                LauncherDbUtils.iterateCursor(c, 0, folderIds);
-            }
+
+            IntArray folderIds = LauncherDbUtils.queryIntArray(db, Favorites.TABLE_NAME,
+                    Favorites._ID, selection, null, null);
             if (!folderIds.isEmpty()) {
                 db.delete(Favorites.TABLE_NAME, Utilities.createDbSelectionQuery(
                         LauncherSettings.Favorites._ID, folderIds), null);
             }
             t.commit();
+            return folderIds;
         } catch (SQLException ex) {
             Log.e(TAG, ex.getMessage(), ex);
-            folderIds.clear();
+            return new IntArray();
         }
-        return folderIds;
     }
 
     /**
@@ -437,7 +448,7 @@
     }
 
     @Thunk static void addModifiedTime(ContentValues values) {
-        values.put(LauncherSettings.ChangeLogColumns.MODIFIED, System.currentTimeMillis());
+        values.put(LauncherSettings.Favorites.MODIFIED, System.currentTimeMillis());
     }
 
     private void clearFlagEmptyDbCreated() {
@@ -502,25 +513,40 @@
      */
     private AutoInstallsLayout createWorkspaceLoaderFromAppRestriction(AppWidgetHost widgetHost) {
         Context ctx = getContext();
-        UserManager um = (UserManager) ctx.getSystemService(Context.USER_SERVICE);
-        Bundle bundle = um.getApplicationRestrictions(ctx.getPackageName());
-        if (bundle == null) {
+        InvariantDeviceProfile grid = LauncherAppState.getIDP(ctx);
+
+        String authority = Settings.Secure.getString(ctx.getContentResolver(),
+                "launcher3.layout.provider");
+        if (TextUtils.isEmpty(authority)) {
             return null;
         }
 
-        String packageName = bundle.getString(RESTRICTION_PACKAGE_NAME);
-        if (packageName != null) {
-            try {
-                Resources targetResources = ctx.getPackageManager()
-                        .getResourcesForApplication(packageName);
-                return AutoInstallsLayout.get(ctx, packageName, targetResources,
-                        widgetHost, mOpenHelper);
-            } catch (NameNotFoundException e) {
-                Log.e(TAG, "Target package for restricted profile not found", e);
-                return null;
-            }
+        ProviderInfo pi = ctx.getPackageManager().resolveContentProvider(authority, 0);
+        if (pi == null) {
+            Log.e(TAG, "No provider found for authority " + authority);
+            return null;
         }
-        return null;
+        Uri uri = new Uri.Builder().scheme("content").authority(authority).path("launcher_layout")
+                .appendQueryParameter("version", "1")
+                .appendQueryParameter("gridWidth", Integer.toString(grid.numColumns))
+                .appendQueryParameter("gridHeight", Integer.toString(grid.numRows))
+                .appendQueryParameter("hotseatSize", Integer.toString(grid.numHotseatIcons))
+                .build();
+
+        try (InputStream in = ctx.getContentResolver().openInputStream(uri)) {
+            // Read the full xml so that we fail early in case of any IO error.
+            String layout = new String(IOUtils.toByteArray(in));
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(new StringReader(layout));
+
+            Log.d(TAG, "Loading layout from " + authority);
+            return new AutoInstallsLayout(ctx, widgetHost, mOpenHelper,
+                    ctx.getPackageManager().getResourcesForApplication(pi.applicationInfo),
+                    () -> parser, AutoInstallsLayout.TAG_WORKSPACE);
+        } catch (Exception e) {
+            Log.e(TAG, "Error getting layout stream from: " + authority , e);
+            return null;
+        }
     }
 
     private DefaultLayoutParser getDefaultLayoutParser(AppWidgetHost widgetHost) {
@@ -540,22 +566,24 @@
      * The class is subclassed in tests to create an in-memory db.
      */
     public static class DatabaseHelper extends NoLocaleSQLiteHelper implements LayoutParserCallback {
+        private final BackupManager mBackupManager;
         private final Handler mWidgetHostResetHandler;
         private final Context mContext;
-        private long mMaxItemId = -1;
-        private long mMaxScreenId = -1;
+        private int mMaxItemId = -1;
+        private int mMaxScreenId = -1;
+        private boolean mBackupTableExists;
 
         DatabaseHelper(Context context, Handler widgetHostResetHandler) {
             this(context, widgetHostResetHandler, LauncherFiles.LAUNCHER_DB);
             // Table creation sometimes fails silently, which leads to a crash loop.
             // This way, we will try to create a table every time after crash, so the device
             // would eventually be able to recover.
-            if (!tableExists(Favorites.TABLE_NAME) || !tableExists(WorkspaceScreens.TABLE_NAME)) {
+            if (!tableExists(getReadableDatabase(), Favorites.TABLE_NAME)) {
                 Log.e(TAG, "Tables are missing after onCreate has been called. Trying to recreate");
                 // This operation is a no-op if the table already exists.
                 addFavoritesTable(getWritableDatabase(), true);
-                addWorkspacesTable(getWritableDatabase(), true);
             }
+            mBackupTableExists = tableExists(getReadableDatabase(), Favorites.BACKUP_TABLE_NAME);
 
             initIds();
         }
@@ -568,6 +596,7 @@
             super(context, tableName, SCHEMA_VERSION);
             mContext = context;
             mWidgetHostResetHandler = widgetHostResetHandler;
+            mBackupManager = new BackupManager(mContext);
         }
 
         protected void initIds() {
@@ -581,18 +610,6 @@
             }
         }
 
-        private boolean tableExists(String tableName) {
-            Cursor c = getReadableDatabase().query(
-                    true, "sqlite_master", new String[] {"tbl_name"},
-                    "tbl_name = ?", new String[] {tableName},
-                    null, null, null, null, null);
-            try {
-                return c.getCount() > 0;
-            } finally {
-                c.close();
-            }
-        }
-
         @Override
         public void onCreate(SQLiteDatabase db) {
             if (LOGD) Log.d(TAG, "creating new launcher database");
@@ -601,13 +618,19 @@
             mMaxScreenId = 0;
 
             addFavoritesTable(db, false);
-            addWorkspacesTable(db, false);
 
             // Fresh and clean launcher DB.
             mMaxItemId = initializeMaxItemId(db);
             onEmptyDbCreated();
         }
 
+        protected void onAddOrDeleteOp(SQLiteDatabase db) {
+            if (mBackupTableExists) {
+                dropTable(db, Favorites.BACKUP_TABLE_NAME);
+                mBackupTableExists = false;
+            }
+        }
+
         /**
          * Overriden in tests.
          */
@@ -623,55 +646,18 @@
             Utilities.getPrefs(mContext).edit().putBoolean(EMPTY_DATABASE_CREATED, true).commit();
         }
 
+        public long getSerialNumberForUser(UserHandle user) {
+            return UserManagerCompat.getInstance(mContext).getSerialNumberForUser(user);
+        }
+
         public long getDefaultUserSerial() {
-            return UserManagerCompat.getInstance(mContext).getSerialNumberForUser(
-                    Process.myUserHandle());
+            return getSerialNumberForUser(Process.myUserHandle());
         }
 
         private void addFavoritesTable(SQLiteDatabase db, boolean optional) {
             Favorites.addTableToDb(db, getDefaultUserSerial(), optional);
         }
 
-        private void addWorkspacesTable(SQLiteDatabase db, boolean optional) {
-            String ifNotExists = optional ? " IF NOT EXISTS " : "";
-            db.execSQL("CREATE TABLE " + ifNotExists + WorkspaceScreens.TABLE_NAME + " (" +
-                    LauncherSettings.WorkspaceScreens._ID + " INTEGER PRIMARY KEY," +
-                    LauncherSettings.WorkspaceScreens.SCREEN_RANK + " INTEGER," +
-                    LauncherSettings.ChangeLogColumns.MODIFIED + " INTEGER NOT NULL DEFAULT 0" +
-                    ");");
-        }
-
-        private void removeOrphanedItems(SQLiteDatabase db) {
-            // Delete items directly on the workspace who's screen id doesn't exist
-            //  "DELETE FROM favorites WHERE screen NOT IN (SELECT _id FROM workspaceScreens)
-            //   AND container = -100"
-            String removeOrphanedDesktopItems = "DELETE FROM " + Favorites.TABLE_NAME +
-                    " WHERE " +
-                    LauncherSettings.Favorites.SCREEN + " NOT IN (SELECT " +
-                    LauncherSettings.WorkspaceScreens._ID + " FROM " + WorkspaceScreens.TABLE_NAME + ")" +
-                    " AND " +
-                    LauncherSettings.Favorites.CONTAINER + " = " +
-                    LauncherSettings.Favorites.CONTAINER_DESKTOP;
-            db.execSQL(removeOrphanedDesktopItems);
-
-            // Delete items contained in folders which no longer exist (after above statement)
-            //  "DELETE FROM favorites  WHERE container <> -100 AND container <> -101 AND container
-            //   NOT IN (SELECT _id FROM favorites WHERE itemType = 2)"
-            String removeOrphanedFolderItems = "DELETE FROM " + Favorites.TABLE_NAME +
-                    " WHERE " +
-                    LauncherSettings.Favorites.CONTAINER + " <> " +
-                    LauncherSettings.Favorites.CONTAINER_DESKTOP +
-                    " AND "
-                    + LauncherSettings.Favorites.CONTAINER + " <> " +
-                    LauncherSettings.Favorites.CONTAINER_HOTSEAT +
-                    " AND "
-                    + LauncherSettings.Favorites.CONTAINER + " NOT IN (SELECT " +
-                    LauncherSettings.Favorites._ID + " FROM " + Favorites.TABLE_NAME +
-                    " WHERE " + LauncherSettings.Favorites.ITEM_TYPE + " = " +
-                    LauncherSettings.Favorites.ITEM_TYPE_FOLDER + ")";
-            db.execSQL(removeOrphanedFolderItems);
-        }
-
         @Override
         public void onOpen(SQLiteDatabase db) {
             super.onOpen(db);
@@ -680,8 +666,7 @@
             if (!schemaFile.exists()) {
                 handleOneTimeDataUpgrade(db);
             }
-            DbDowngradeHelper.updateSchemaFile(schemaFile, SCHEMA_VERSION, mContext,
-                    R.raw.downgrade_schema);
+            DbDowngradeHelper.updateSchemaFile(schemaFile, SCHEMA_VERSION, mContext);
         }
 
         /**
@@ -708,12 +693,8 @@
             switch (oldVersion) {
                 // The version cannot be lower that 12, as Launcher3 never supported a lower
                 // version of the DB.
-                case 12: {
-                    // With the new shrink-wrapped and re-orderable workspaces, it makes sense
-                    // to persist workspace screens and their relative order.
-                    mMaxScreenId = 0;
-                    addWorkspacesTable(db, false);
-                }
+                case 12:
+                    // No-op
                 case 13: {
                     try (SQLiteTransaction t = new SQLiteTransaction(db)) {
                         // Insert new column for holding widget provider name
@@ -727,15 +708,7 @@
                     }
                 }
                 case 14: {
-                    try (SQLiteTransaction t = new SQLiteTransaction(db)) {
-                        // Insert new column for holding update timestamp
-                        db.execSQL("ALTER TABLE favorites " +
-                                "ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;");
-                        db.execSQL("ALTER TABLE workspaceScreens " +
-                                "ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;");
-                        t.commit();
-                    } catch (SQLException ex) {
-                        Log.e(TAG, ex.getMessage(), ex);
+                    if (!addIntegerColumn(db, Favorites.MODIFIED, 0)) {
                         // Old version remains, which means we wipe old data
                         break;
                     }
@@ -746,22 +719,15 @@
                         break;
                     }
                 }
-                case 16: {
+                case 16:
                     // No-op
-                }
-                case 17: {
+                case 17:
                     // No-op
-                }
-                case 18: {
-                    // Due to a data loss bug, some users may have items associated with screen ids
-                    // which no longer exist. Since this can cause other problems, and since the user
-                    // will never see these items anyway, we use database upgrade as an opportunity to
-                    // clean things up.
-                    removeOrphanedItems(db);
-                }
+                case 18:
+                    // No-op
                 case 19: {
                     // Add userId column
-                    if (!addProfileColumn(db)) {
+                    if (!addIntegerColumn(db, Favorites.PROFILE_ID, getDefaultUserSerial())) {
                         // Old version remains, which means we wipe old data
                         break;
                     }
@@ -771,10 +737,7 @@
                         break;
                     }
                 case 21:
-                    // Recreate workspace table with screen id a primary key
-                    if (!recreateWorkspaceTable(db)) {
-                        break;
-                    }
+                    // No-op
                 case 22: {
                     if (!addIntegerColumn(db, Favorites.OPTIONS, 0)) {
                         // Old version remains, which means we wipe old data
@@ -793,7 +756,30 @@
                             !LauncherDbUtils.prepareScreenZeroToHostQsb(mContext, db)) {
                         break;
                     }
-                case 27:
+                case 27: {
+                    // Update the favorites table so that the screen ids are ordered based on
+                    // workspace page rank.
+                    IntArray finalScreens = LauncherDbUtils.queryIntArray(db, "workspaceScreens",
+                            BaseColumns._ID, null, null, "screenRank");
+                    int[] original = finalScreens.toArray();
+                    Arrays.sort(original);
+                    String updatemap = "";
+                    for (int i = 0; i < original.length; i++) {
+                        if (finalScreens.get(i) != original[i]) {
+                            updatemap += String.format(Locale.ENGLISH, " WHEN %1$s=%2$d THEN %3$d",
+                                    Favorites.SCREEN, finalScreens.get(i), original[i]);
+                        }
+                    }
+                    if (!TextUtils.isEmpty(updatemap)) {
+                        String query = String.format(Locale.ENGLISH,
+                                "UPDATE %1$s SET %2$s=CASE %3$s ELSE %2$s END WHERE %4$s = %5$d",
+                                Favorites.TABLE_NAME, Favorites.SCREEN, updatemap,
+                                Favorites.CONTAINER, Favorites.CONTAINER_DESKTOP);
+                        db.execSQL(query);
+                    }
+                    dropTable(db, "workspaceScreens");
+                }
+                case 28:
                     // DB Upgraded successfully
                     return;
             }
@@ -820,8 +806,8 @@
          */
         public void createEmptyDB(SQLiteDatabase db) {
             try (SQLiteTransaction t = new SQLiteTransaction(db)) {
-                db.execSQL("DROP TABLE IF EXISTS " + Favorites.TABLE_NAME);
-                db.execSQL("DROP TABLE IF EXISTS " + WorkspaceScreens.TABLE_NAME);
+                dropTable(db, Favorites.TABLE_NAME);
+                dropTable(db, "workspaceScreens");
                 onCreate(db);
                 t.commit();
             }
@@ -844,17 +830,9 @@
                 Log.e(TAG, "getAppWidgetIds not supported", e);
                 return;
             }
-            final HashSet<Integer> validWidgets = new HashSet<>();
-            try (Cursor c = db.query(Favorites.TABLE_NAME,
-                    new String[] {Favorites.APPWIDGET_ID },
-                    "itemType=" + Favorites.ITEM_TYPE_APPWIDGET, null, null, null, null)) {
-                while (c.moveToNext()) {
-                    validWidgets.add(c.getInt(0));
-                }
-            } catch (SQLException ex) {
-                Log.w(TAG, "Error getting widgets list", ex);
-                return;
-            }
+            final IntSet validWidgets = IntSet.wrap(LauncherDbUtils.queryIntArray(db,
+                    Favorites.TABLE_NAME, Favorites.APPWIDGET_ID,
+                    "itemType=" + Favorites.ITEM_TYPE_APPWIDGET, null, null));
             for (int widgetId : allWidgets) {
                 if (!validWidgets.contains(widgetId)) {
                     try {
@@ -899,7 +877,7 @@
                         continue;
                     }
 
-                    long id = c.getLong(idIndex);
+                    int id = c.getInt(idIndex);
                     updateStmt.bindLong(1, id);
                     updateStmt.executeUpdateDelete();
                 }
@@ -909,43 +887,6 @@
             }
         }
 
-        /**
-         * Recreates workspace table and migrates data to the new table.
-         */
-        public boolean recreateWorkspaceTable(SQLiteDatabase db) {
-            try (SQLiteTransaction t = new SQLiteTransaction(db)) {
-                final ArrayList<Long> sortedIDs;
-
-                try (Cursor c = db.query(WorkspaceScreens.TABLE_NAME,
-                        new String[] {LauncherSettings.WorkspaceScreens._ID},
-                        null, null, null, null,
-                        LauncherSettings.WorkspaceScreens.SCREEN_RANK)) {
-                    // Use LinkedHashSet so that ordering is preserved
-                    sortedIDs = new ArrayList<>(
-                            LauncherDbUtils.iterateCursor(c, 0, new LinkedHashSet<Long>()));
-                }
-                db.execSQL("DROP TABLE IF EXISTS " + WorkspaceScreens.TABLE_NAME);
-                addWorkspacesTable(db, false);
-
-                // Add all screen ids back
-                int total = sortedIDs.size();
-                for (int i = 0; i < total; i++) {
-                    ContentValues values = new ContentValues();
-                    values.put(LauncherSettings.WorkspaceScreens._ID, sortedIDs.get(i));
-                    values.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
-                    addModifiedTime(values);
-                    db.insertOrThrow(WorkspaceScreens.TABLE_NAME, null, values);
-                }
-                t.commit();
-                mMaxScreenId = sortedIDs.isEmpty() ? 0 : Collections.max(sortedIDs);
-            } catch (SQLException ex) {
-                // Old version remains, which means we wipe old data
-                Log.e(TAG, ex.getMessage(), ex);
-                return false;
-            }
-            return true;
-        }
-
         @Thunk boolean updateFolderItemsRank(SQLiteDatabase db, boolean addRankColumn) {
             try (SQLiteTransaction t = new SQLiteTransaction(db)) {
                 if (addRankColumn) {
@@ -975,10 +916,6 @@
             return true;
         }
 
-        private boolean addProfileColumn(SQLiteDatabase db) {
-            return addIntegerColumn(db, Favorites.PROFILE_ID, getDefaultUserSerial());
-        }
-
         private boolean addIntegerColumn(SQLiteDatabase db, String columnName, long defaultValue) {
             try (SQLiteTransaction t = new SQLiteTransaction(db)) {
                 db.execSQL("ALTER TABLE favorites ADD COLUMN "
@@ -997,7 +934,7 @@
         // constructor is called, and we only pass a reference to LauncherProvider to LauncherApp
         // after that point
         @Override
-        public long generateNewItemId() {
+        public int generateNewItemId() {
             if (mMaxItemId < 0) {
                 throw new RuntimeException("Error: max item id was not initialized");
             }
@@ -1010,21 +947,24 @@
         }
 
         @Override
-        public long insertAndCheck(SQLiteDatabase db, ContentValues values) {
+        public int insertAndCheck(SQLiteDatabase db, ContentValues values) {
             return dbInsertAndCheck(this, db, Favorites.TABLE_NAME, null, values);
         }
 
-        public void checkId(String table, ContentValues values) {
-            long id = values.getAsLong(LauncherSettings.BaseLauncherColumns._ID);
-            if (WorkspaceScreens.TABLE_NAME.equals(table)) {
-                mMaxScreenId = Math.max(id, mMaxScreenId);
-            }  else {
-                mMaxItemId = Math.max(id, mMaxItemId);
+        public void checkId(ContentValues values) {
+            int id = values.getAsInteger(Favorites._ID);
+            mMaxItemId = Math.max(id, mMaxItemId);
+
+            Integer screen = values.getAsInteger(Favorites.SCREEN);
+            Integer container = values.getAsInteger(Favorites.CONTAINER);
+            if (screen != null && container != null
+                    && container.intValue() == Favorites.CONTAINER_DESKTOP) {
+                mMaxScreenId = Math.max(screen, mMaxScreenId);
             }
         }
 
-        private long initializeMaxItemId(SQLiteDatabase db) {
-            return getMaxId(db, Favorites.TABLE_NAME);
+        private int initializeMaxItemId(SQLiteDatabase db) {
+            return getMaxId(db, "SELECT MAX(%1$s) FROM %2$s", Favorites._ID, Favorites.TABLE_NAME);
         }
 
         // Generates a new ID to use for an workspace screen in your database. This method
@@ -1032,7 +972,7 @@
         // call the constructor from the worker thread; however, this doesn't extend until after the
         // constructor is called, and we only pass a reference to LauncherProvider to LauncherApp
         // after that point
-        public long generateNewScreenId() {
+        public int generateNewScreenId() {
             if (mMaxScreenId < 0) {
                 throw new RuntimeException("Error: max screen id was not initialized");
             }
@@ -1040,34 +980,19 @@
             return mMaxScreenId;
         }
 
-        private long initializeMaxScreenId(SQLiteDatabase db) {
-            return getMaxId(db, WorkspaceScreens.TABLE_NAME);
+        private int initializeMaxScreenId(SQLiteDatabase db) {
+            return getMaxId(db, "SELECT MAX(%1$s) FROM %2$s WHERE %3$s = %4$d",
+                    Favorites.SCREEN, Favorites.TABLE_NAME, Favorites.CONTAINER,
+                    Favorites.CONTAINER_DESKTOP);
         }
 
         @Thunk int loadFavorites(SQLiteDatabase db, AutoInstallsLayout loader) {
-            ArrayList<Long> screenIds = new ArrayList<Long>();
             // TODO: Use multiple loaders with fall-back and transaction.
-            int count = loader.loadLayout(db, screenIds);
-
-            // Add the screens specified by the items above
-            Collections.sort(screenIds);
-            int rank = 0;
-            ContentValues values = new ContentValues();
-            for (Long id : screenIds) {
-                values.clear();
-                values.put(LauncherSettings.WorkspaceScreens._ID, id);
-                values.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, rank);
-                if (dbInsertAndCheck(this, db, WorkspaceScreens.TABLE_NAME, null, values) < 0) {
-                    throw new RuntimeException("Failed initialize screen table"
-                            + "from default layout");
-                }
-                rank++;
-            }
+            int count = loader.loadLayout(db, new IntArray());
 
             // Ensure that the max ids are initialized
             mMaxItemId = initializeMaxItemId(db);
             mMaxScreenId = initializeMaxScreenId(db);
-
             return count;
         }
     }
@@ -1075,22 +1000,14 @@
     /**
      * @return the max _id in the provided table.
      */
-    @Thunk static long getMaxId(SQLiteDatabase db, String table) {
-        Cursor c = db.rawQuery("SELECT MAX(_id) FROM " + table, null);
-        // get the result
-        long id = -1;
-        if (c != null && c.moveToNext()) {
-            id = c.getLong(0);
+    @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");
         }
-        if (c != null) {
-            c.close();
-        }
-
-        if (id == -1) {
-            throw new RuntimeException("Error: could not query max id in " + table);
-        }
-
-        return id;
+        return max;
     }
 
     static class SqlArguments {
diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
index 3cf6d62..f964b8d 100644
--- a/src/com/android/launcher3/LauncherRootView.java
+++ b/src/com/android/launcher3/LauncherRootView.java
@@ -8,14 +8,22 @@
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.Insets;
 import android.graphics.Paint;
 import android.graphics.Rect;
+import android.os.Build;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewDebug;
+import android.view.WindowInsets;
+
+import java.util.Collections;
+import java.util.List;
 
 public class LauncherRootView extends InsettableFrameLayout {
 
+    private final Rect mTempRect = new Rect();
+
     private final Launcher mLauncher;
 
     private final Paint mOpaquePaint;
@@ -23,8 +31,14 @@
     @ViewDebug.ExportedProperty(category = "launcher")
     private final Rect mConsumedInsets = new Rect();
 
+    @ViewDebug.ExportedProperty(category = "launcher")
+    private static final List<Rect> SYSTEM_GESTURE_EXCLUSION_RECT =
+            Collections.singletonList(new Rect());
+
     private View mAlignedView;
     private WindowStateListener mWindowStateListener;
+    @ViewDebug.ExportedProperty(category = "launcher")
+    private boolean mDisallowBackGesture;
 
     public LauncherRootView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -46,24 +60,21 @@
         super.onFinishInflate();
     }
 
-    @TargetApi(23)
-    @Override
-    protected boolean fitSystemWindows(Rect insets) {
+    private void handleSystemWindowInsets(Rect insets) {
         mConsumedInsets.setEmpty();
         boolean drawInsetBar = false;
-        if (mLauncher.isInMultiWindowModeCompat()
+        if (mLauncher.isInMultiWindowMode()
                 && (insets.left > 0 || insets.right > 0 || insets.bottom > 0)) {
             mConsumedInsets.left = insets.left;
             mConsumedInsets.right = insets.right;
             mConsumedInsets.bottom = insets.bottom;
-            insets = new Rect(0, insets.top, 0, 0);
+            insets.set(0, insets.top, 0, 0);
             drawInsetBar = true;
         } else  if ((insets.right > 0 || insets.left > 0) &&
-                (!Utilities.ATLEAST_MARSHMALLOW ||
-                        getContext().getSystemService(ActivityManager.class).isLowRamDevice())) {
+                getContext().getSystemService(ActivityManager.class).isLowRamDevice()) {
             mConsumedInsets.left = insets.left;
             mConsumedInsets.right = insets.right;
-            insets = new Rect(0, insets.top, 0, insets.bottom);
+            insets.set(0, insets.top, 0, insets.bottom);
             drawInsetBar = true;
         }
 
@@ -71,7 +82,7 @@
                 UI_STATE_ROOT_VIEW, drawInsetBar ? FLAG_DARK_NAV : 0);
 
         // Update device profile before notifying th children.
-        mLauncher.getDeviceProfile().updateInsets(insets);
+        mLauncher.updateInsets(insets);
         boolean resetState = !insets.equals(mInsets);
         setInsets(insets);
 
@@ -90,8 +101,19 @@
         if (resetState) {
             mLauncher.getStateManager().reapplyState(true /* cancelCurrentAnimation */);
         }
+    }
 
-        return true; // I'll take it from here
+    @Override
+    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+        mTempRect.set(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(),
+                insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom());
+        handleSystemWindowInsets(mTempRect);
+        if (Utilities.ATLEAST_Q) {
+            return insets.inset(mConsumedInsets.left, mConsumedInsets.top,
+                    mConsumedInsets.right, mConsumedInsets.bottom);
+        } else {
+            return insets.replaceSystemWindowInsets(mTempRect);
+        }
     }
 
     @Override
@@ -104,7 +126,7 @@
     }
 
     public void dispatchInsets() {
-        mLauncher.getDeviceProfile().updateInsets(mInsets);
+        mLauncher.updateInsets(mInsets);
         super.setInsets(mInsets);
     }
 
@@ -146,6 +168,24 @@
         }
     }
 
+    @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);
+    }
+
+    @TargetApi(Build.VERSION_CODES.Q)
+    public void setDisallowBackGesture(boolean disallowBackGesture) {
+        if (!Utilities.ATLEAST_Q) {
+            return;
+        }
+        mDisallowBackGesture = disallowBackGesture;
+        setSystemGestureExclusionRects(mDisallowBackGesture
+                ? SYSTEM_GESTURE_EXCLUSION_RECT
+                : Collections.emptyList());
+    }
+
     public interface WindowStateListener {
 
         void onWindowFocusChanged(boolean hasFocus);
diff --git a/src/com/android/launcher3/LauncherScroller.java b/src/com/android/launcher3/LauncherScroller.java
deleted file mode 100644
index a9b4955..0000000
--- a/src/com/android/launcher3/LauncherScroller.java
+++ /dev/null
@@ -1,558 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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.animation.TimeInterpolator;
-import android.content.Context;
-import android.hardware.SensorManager;
-import android.os.Build;
-import android.view.ViewConfiguration;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-
-/**
- * This class differs from the framework {@link android.widget.Scroller} in that
- * you can modify the Interpolator post-construction.
- */
-public class LauncherScroller  {
-    private int mMode;
-
-    private int mStartX;
-    private int mStartY;
-    private int mFinalX;
-    private int mFinalY;
-
-    private int mMinX;
-    private int mMaxX;
-    private int mMinY;
-    private int mMaxY;
-
-    private int mCurrX;
-    private int mCurrY;
-    private long mStartTime;
-    private int mDuration;
-    private float mDurationReciprocal;
-    private float mDeltaX;
-    private float mDeltaY;
-    private boolean mFinished;
-    private TimeInterpolator mInterpolator;
-    private boolean mFlywheel;
-
-    private float mVelocity;
-    private float mCurrVelocity;
-    private int mDistance;
-
-    private float mFlingFriction = ViewConfiguration.getScrollFriction();
-
-    private static final int DEFAULT_DURATION = 250;
-    private static final int SCROLL_MODE = 0;
-    private static final int FLING_MODE = 1;
-
-    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 float mDeceleration;
-    private final float mPpi;
-
-    // A context-specific coefficient adjusted to physical values.
-    private float mPhysicalCoeff;
-
-    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;
-
-        // This controls the viscous fluid effect (how much of it)
-        sViscousFluidScale = 8.0f;
-        // must be set to 1.0 (used in viscousFluid())
-        sViscousFluidNormalize = 1.0f;
-        sViscousFluidNormalize = 1.0f / viscousFluid(1.0f);
-
-    }
-
-    private static float sViscousFluidScale;
-    private static float sViscousFluidNormalize;
-
-    public void setInterpolator(TimeInterpolator interpolator) {
-        mInterpolator = interpolator;
-    }
-
-    /**
-     * Create a Scroller with the default duration and interpolator.
-     */
-    public LauncherScroller(Context context) {
-        this(context, null);
-    }
-
-    /**
-     * Create a Scroller with the specified interpolator. If the interpolator is
-     * null, the default (viscous) interpolator will be used. "Flywheel" behavior will
-     * be in effect for apps targeting Honeycomb or newer.
-     */
-    public LauncherScroller(Context context, Interpolator interpolator) {
-        this(context, interpolator,
-                context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB);
-    }
-
-    /**
-     * Create a Scroller with the specified interpolator. If the interpolator is
-     * null, the default (viscous) interpolator will be used. Specify whether or
-     * not to support progressive "flywheel" behavior in flinging.
-     */
-    public LauncherScroller(Context context, Interpolator interpolator, boolean flywheel) {
-        mFinished = true;
-        mInterpolator = interpolator;
-        mPpi = context.getResources().getDisplayMetrics().density * 160.0f;
-        mDeceleration = computeDeceleration(ViewConfiguration.getScrollFriction());
-        mFlywheel = flywheel;
-
-        mPhysicalCoeff = computeDeceleration(0.84f); // look and feel tuning
-    }
-
-    /**
-     * 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) {
-        mDeceleration = computeDeceleration(friction);
-        mFlingFriction = friction;
-    }
-
-    private float computeDeceleration(float friction) {
-        return SensorManager.GRAVITY_EARTH   // g (m/s^2)
-                      * 39.37f               // inch/meter
-                      * mPpi                 // pixels per inch
-                      * friction;
-    }
-
-    /**
-     *
-     * Returns whether the scroller has finished scrolling.
-     *
-     * @return True if the scroller has finished scrolling, false otherwise.
-     */
-    public final boolean isFinished() {
-        return mFinished;
-    }
-
-    /**
-     * Force the finished field to a particular value.
-     *
-     * @param finished The new finished value.
-     */
-    public final void forceFinished(boolean finished) {
-        mFinished = finished;
-    }
-
-    /**
-     * Returns how long the scroll event will take, in milliseconds.
-     *
-     * @return The duration of the scroll in milliseconds.
-     */
-    public final int getDuration() {
-        return mDuration;
-    }
-
-    /**
-     * Returns the current X offset in the scroll.
-     *
-     * @return The new X offset as an absolute distance from the origin.
-     */
-    public final int getCurrX() {
-        return mCurrX;
-    }
-
-    /**
-     * Returns the current Y offset in the scroll.
-     *
-     * @return The new Y offset as an absolute distance from the origin.
-     */
-    public final int getCurrY() {
-        return mCurrY;
-    }
-
-    /**
-     * Returns the current velocity.
-     *
-     * @return The original velocity less the deceleration. Result may be
-     * negative.
-     */
-    public float getCurrVelocity() {
-        return mMode == FLING_MODE ?
-                mCurrVelocity : mVelocity - mDeceleration * timePassed() / 2000.0f;
-    }
-
-    /**
-     * Returns the start X offset in the scroll.
-     *
-     * @return The start X offset as an absolute distance from the origin.
-     */
-    public final int getStartX() {
-        return mStartX;
-    }
-
-    /**
-     * Returns the start Y offset in the scroll.
-     *
-     * @return The start Y offset as an absolute distance from the origin.
-     */
-    public final int getStartY() {
-        return mStartY;
-    }
-
-    /**
-     * Returns where the scroll will end. Valid only for "fling" scrolls.
-     *
-     * @return The final X offset as an absolute distance from the origin.
-     */
-    public final int getFinalX() {
-        return mFinalX;
-    }
-
-    /**
-     * Returns where the scroll will end. Valid only for "fling" scrolls.
-     *
-     * @return The final Y offset as an absolute distance from the origin.
-     */
-    public final int getFinalY() {
-        return mFinalY;
-    }
-
-    /**
-     * Call this when you want to know the new location.  If it returns true,
-     * the animation is not yet finished.
-     */
-    public boolean computeScrollOffset() {
-        if (mFinished) {
-            return false;
-        }
-
-        int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
-
-        if (timePassed < mDuration) {
-            switch (mMode) {
-            case SCROLL_MODE:
-                float x = timePassed * mDurationReciprocal;
-
-                if (mInterpolator == null)
-                    x = viscousFluid(x);
-                else
-                    x = mInterpolator.getInterpolation(x);
-
-                mCurrX = mStartX + Math.round(x * mDeltaX);
-                mCurrY = mStartY + Math.round(x * mDeltaY);
-                break;
-            case FLING_MODE:
-                final float t = (float) timePassed / mDuration;
-                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;
-                }
-
-                mCurrVelocity = velocityCoef * mDistance / mDuration * 1000.0f;
-
-                mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX));
-                // Pin to mMinX <= mCurrX <= mMaxX
-                mCurrX = Math.min(mCurrX, mMaxX);
-                mCurrX = Math.max(mCurrX, mMinX);
-
-                mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY));
-                // Pin to mMinY <= mCurrY <= mMaxY
-                mCurrY = Math.min(mCurrY, mMaxY);
-                mCurrY = Math.max(mCurrY, mMinY);
-
-                if (mCurrX == mFinalX && mCurrY == mFinalY) {
-                    mFinished = true;
-                }
-
-                break;
-            }
-        }
-        else {
-            mCurrX = mFinalX;
-            mCurrY = mFinalY;
-            mFinished = true;
-        }
-        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 startX Starting horizontal scroll offset in pixels. Positive
-     *        numbers will scroll the content to the left.
-     * @param startY Starting vertical scroll offset in pixels. Positive numbers
-     *        will scroll the content up.
-     * @param dx Horizontal distance to travel. Positive numbers will scroll the
-     *        content to the left.
-     * @param dy Vertical distance to travel. Positive numbers will scroll the
-     *        content up.
-     */
-    public void startScroll(int startX, int startY, int dx, int dy) {
-        startScroll(startX, startY, dx, dy, DEFAULT_DURATION);
-    }
-
-    /**
-     * Start scrolling by providing a starting point, the distance to travel,
-     * and the duration of the scroll.
-     *
-     * @param startX Starting horizontal scroll offset in pixels. Positive
-     *        numbers will scroll the content to the left.
-     * @param startY Starting vertical scroll offset in pixels. Positive numbers
-     *        will scroll the content up.
-     * @param dx Horizontal distance to travel. Positive numbers will scroll the
-     *        content to the left.
-     * @param dy Vertical distance to travel. Positive numbers will scroll the
-     *        content up.
-     * @param duration Duration of the scroll in milliseconds.
-     */
-    public void startScroll(int startX, int startY, int dx, int dy, int duration) {
-        mMode = SCROLL_MODE;
-        mFinished = false;
-        mDuration = duration;
-        mStartTime = AnimationUtils.currentAnimationTimeMillis();
-        mStartX = startX;
-        mStartY = startY;
-        mFinalX = startX + dx;
-        mFinalY = startY + dy;
-        mDeltaX = dx;
-        mDeltaY = dy;
-        mDurationReciprocal = 1.0f / (float) mDuration;
-    }
-
-    /**
-     * Start scrolling based on a fling gesture. The distance travelled will
-     * depend on the initial velocity of the fling.
-     *
-     * @param startX Starting point of the scroll (X)
-     * @param startY Starting point of the scroll (Y)
-     * @param velocityX Initial velocity of the fling (X) measured in pixels per
-     *        second.
-     * @param velocityY Initial velocity of the fling (Y) measured in pixels per
-     *        second
-     * @param minX Minimum X value. The scroller will not scroll past this
-     *        point.
-     * @param maxX Maximum X value. The scroller will not scroll past this
-     *        point.
-     * @param minY Minimum Y value. The scroller will not scroll past this
-     *        point.
-     * @param maxY Maximum Y value. The scroller will not scroll past this
-     *        point.
-     */
-    public void fling(int startX, int startY, int velocityX, int velocityY,
-            int minX, int maxX, int minY, int maxY) {
-        // Continue a scroll or fling in progress
-        if (mFlywheel && !mFinished) {
-            float oldVel = getCurrVelocity();
-
-            float dx = (float) (mFinalX - mStartX);
-            float dy = (float) (mFinalY - mStartY);
-            float hyp = (float) Math.hypot(dx, dy);
-
-            float ndx = dx / hyp;
-            float ndy = dy / hyp;
-
-            float oldVelocityX = ndx * oldVel;
-            float oldVelocityY = ndy * oldVel;
-            if (Math.signum(velocityX) == Math.signum(oldVelocityX) &&
-                    Math.signum(velocityY) == Math.signum(oldVelocityY)) {
-                velocityX += oldVelocityX;
-                velocityY += oldVelocityY;
-            }
-        }
-
-        mMode = FLING_MODE;
-        mFinished = false;
-
-        float velocity = (float) Math.hypot(velocityX, velocityY);
-
-        mVelocity = velocity;
-        mDuration = getSplineFlingDuration(velocity);
-        mStartTime = AnimationUtils.currentAnimationTimeMillis();
-        mStartX = startX;
-        mStartY = startY;
-
-        float coeffX = velocity == 0 ? 1.0f : velocityX / velocity;
-        float coeffY = velocity == 0 ? 1.0f : velocityY / velocity;
-
-        double totalDistance = getSplineFlingDistance(velocity);
-        mDistance = (int) (totalDistance * Math.signum(velocity));
-
-        mMinX = minX;
-        mMaxX = maxX;
-        mMinY = minY;
-        mMaxY = maxY;
-
-        mFinalX = startX + (int) Math.round(totalDistance * coeffX);
-        // Pin to mMinX <= mFinalX <= mMaxX
-        mFinalX = Math.min(mFinalX, mMaxX);
-        mFinalX = Math.max(mFinalX, mMinX);
-
-        mFinalY = startY + (int) Math.round(totalDistance * coeffY);
-        // Pin to mMinY <= mFinalY <= mMaxY
-        mFinalY = Math.min(mFinalY, mMaxY);
-        mFinalY = Math.max(mFinalY, mMinY);
-    }
-
-    private double getSplineDeceleration(float velocity) {
-        return Math.log(INFLEXION * Math.abs(velocity) / (mFlingFriction * mPhysicalCoeff));
-    }
-
-    private int getSplineFlingDuration(float velocity) {
-        final double l = getSplineDeceleration(velocity);
-        final double decelMinusOne = DECELERATION_RATE - 1.0;
-        return (int) (1000.0 * Math.exp(l / decelMinusOne));
-    }
-
-    private double getSplineFlingDistance(float velocity) {
-        final double l = getSplineDeceleration(velocity);
-        final double decelMinusOne = DECELERATION_RATE - 1.0;
-        return mFlingFriction * mPhysicalCoeff * Math.exp(DECELERATION_RATE / decelMinusOne * l);
-    }
-
-    static float viscousFluid(float x)
-    {
-        x *= sViscousFluidScale;
-        if (x < 1.0f) {
-            x -= (1.0f - (float)Math.exp(-x));
-        } else {
-            float start = 0.36787944117f;   // 1/e == exp(-1)
-            x = 1.0f - (float)Math.exp(1.0f - x);
-            x = start + x * (1.0f - start);
-        }
-        x *= sViscousFluidNormalize;
-        return x;
-    }
-
-    /**
-     * Stops the animation. Contrary to {@link #forceFinished(boolean)},
-     * aborting the animating cause the scroller to move to the final x and y
-     * position
-     *
-     * @see #forceFinished(boolean)
-     */
-    public void abortAnimation() {
-        mCurrX = mFinalX;
-        mCurrY = mFinalY;
-        mFinished = true;
-    }
-
-    /**
-     * Extend the scroll animation. This allows a running animation to scroll
-     * further and longer, when used with {@link #setFinalX(int)} or {@link #setFinalY(int)}.
-     *
-     * @param extend Additional time to scroll in milliseconds.
-     * @see #setFinalX(int)
-     * @see #setFinalY(int)
-     */
-    public void extendDuration(int extend) {
-        int passed = timePassed();
-        mDuration = passed + extend;
-        mDurationReciprocal = 1.0f / mDuration;
-        mFinished = false;
-    }
-
-    /**
-     * Returns the time elapsed since the beginning of the scrolling.
-     *
-     * @return The elapsed time in milliseconds.
-     */
-    public int timePassed() {
-        return (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
-    }
-
-    /**
-     * Sets the final position (X) for this scroller.
-     *
-     * @param newX The new X offset as an absolute distance from the origin.
-     * @see #extendDuration(int)
-     * @see #setFinalY(int)
-     */
-    public void setFinalX(int newX) {
-        mFinalX = newX;
-        mDeltaX = mFinalX - mStartX;
-        mFinished = false;
-    }
-
-    /**
-     * Sets the final position (Y) for this scroller.
-     *
-     * @param newY The new Y offset as an absolute distance from the origin.
-     * @see #extendDuration(int)
-     * @see #setFinalX(int)
-     */
-    public void setFinalY(int newY) {
-        mFinalY = newY;
-        mDeltaY = mFinalY - mStartY;
-        mFinished = false;
-    }
-
-    /**
-     * @hide
-     */
-    public boolean isScrollingInDirection(float xvel, float yvel) {
-        return !mFinished && Math.signum(xvel) == Math.signum(mFinalX - mStartX) &&
-                Math.signum(yvel) == Math.signum(mFinalY - mStartY);
-    }
-}
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 3b337ef..e248ba0 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -26,16 +26,17 @@
  * Settings related utilities.
  */
 public class LauncherSettings {
-    /** Columns required on table staht will be subject to backup and restore. */
-    static interface ChangeLogColumns extends BaseColumns {
+
+    /**
+     * Favorites.
+     */
+    public static final class Favorites implements BaseColumns {
         /**
          * The time of the last update to this row.
          * <P>Type: INTEGER</P>
          */
         public static final String MODIFIED = "modified";
-    }
 
-    static public interface BaseLauncherColumns extends ChangeLogColumns {
         /**
          * Descriptive name of the gesture that can be displayed to the user.
          * <P>Type: TEXT</P>
@@ -84,38 +85,15 @@
          * <P>Type: BLOB</P>
          */
         public static final String ICON = "icon";
-    }
-
-    /**
-     * Workspace Screens.
-     *
-     * Tracks the order of workspace screens.
-     */
-    public static final class WorkspaceScreens implements ChangeLogColumns {
-
-        public static final String TABLE_NAME = "workspaceScreens";
-
-        /**
-         * The content:// style URL for this table
-         */
-        public static final Uri CONTENT_URI = Uri.parse("content://" +
-                LauncherProvider.AUTHORITY + "/" + TABLE_NAME);
-
-        /**
-         * The rank of this screen -- ie. how it is ordered relative to the other screens.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String SCREEN_RANK = "screenRank";
-    }
-
-    /**
-     * Favorites.
-     */
-    public static final class Favorites implements BaseLauncherColumns {
 
         public static final String TABLE_NAME = "favorites";
 
         /**
+         * Backup table created when when the favorites table is modified during grid migration
+         */
+        public static final String BACKUP_TABLE_NAME = "favorites_bakup";
+
+        /**
          * The content:// style URL for this table
          */
         public static final Uri CONTENT_URI = Uri.parse("content://" +
@@ -128,7 +106,7 @@
          *
          * @return The unique content URL for the specified row.
          */
-        public static Uri getContentUri(long id) {
+        public static Uri getContentUri(int id) {
             return Uri.parse("content://" + LauncherProvider.AUTHORITY +
                     "/" + TABLE_NAME + "/" + id);
         }
@@ -258,8 +236,13 @@
         public static final String OPTIONS = "options";
 
         public static void addTableToDb(SQLiteDatabase db, long myProfileId, boolean optional) {
+            addTableToDb(db, myProfileId, optional, TABLE_NAME);
+        }
+
+        public static void addTableToDb(SQLiteDatabase db, long myProfileId, boolean optional,
+                String tableName) {
             String ifNotExists = optional ? " IF NOT EXISTS " : "";
-            db.execSQL("CREATE TABLE " + ifNotExists + TABLE_NAME + " (" +
+            db.execSQL("CREATE TABLE " + ifNotExists + tableName + " (" +
                     "_id INTEGER PRIMARY KEY," +
                     "title TEXT," +
                     "intent TEXT," +
@@ -306,6 +289,10 @@
 
         public static final String METHOD_REMOVE_GHOST_WIDGETS = "remove_ghost_widgets";
 
+        public static final String METHOD_NEW_TRANSACTION = "new_db_transaction";
+
+        public static final String METHOD_REFRESH_BACKUP_TABLE = "refresh_backup_table";
+
         public static final String EXTRA_VALUE = "value";
 
         public static Bundle call(ContentResolver cr, String method) {
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 8a15b24..dcfd272 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -17,19 +17,35 @@
 
 import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
 import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
+import static android.view.View.VISIBLE;
 import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
 
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
+import static com.android.launcher3.anim.Interpolators.ACCEL;
+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.clampToProgress;
+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.NORMAL_STATE_ORDINAL;
+import static com.android.launcher3.testing.TestProtocol.OVERVIEW_PEEK_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.testing.TestProtocol.SPRING_LOADED_STATE_ORDINAL;
 import static com.android.launcher3.anim.Interpolators.ACCEL_2;
 import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
 
-import android.graphics.Rect;
 import android.view.animation.Interpolator;
 
+import com.android.launcher3.anim.AnimatorSetBuilder;
 import com.android.launcher3.states.SpringLoadedState;
-import com.android.launcher3.uioverrides.AllAppsState;
-import com.android.launcher3.uioverrides.FastOverviewState;
-import com.android.launcher3.uioverrides.OverviewState;
 import com.android.launcher3.uioverrides.UiFactory;
+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;
@@ -52,6 +68,7 @@
     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 RECENTS_CLEAR_ALL_BUTTON = 1 << 6;
 
     protected static final int FLAG_MULTI_PAGE = 1 << 0;
     protected static final int FLAG_DISABLE_ACCESSIBILITY = 1 << 1;
@@ -72,24 +89,30 @@
                 }
             };
 
-    private static final LauncherState[] sAllStates = new LauncherState[5];
+    private static final LauncherState[] sAllStates = new LauncherState[7];
 
     /**
      * TODO: Create a separate class for NORMAL state.
      */
-    public static final LauncherState NORMAL = new LauncherState(0, ContainerType.WORKSPACE, 0,
+    public static final LauncherState NORMAL = new LauncherState(NORMAL_STATE_ORDINAL,
+            ContainerType.WORKSPACE, 0,
             FLAG_DISABLE_RESTORE | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED | FLAG_HIDE_BACK_BUTTON |
             FLAG_HAS_SYS_UI_SCRIM);
 
     /**
      * Various Launcher states arranged in the increasing order of UI layers
      */
-    public static final LauncherState SPRING_LOADED = new SpringLoadedState(1);
-    public static final LauncherState OVERVIEW = new OverviewState(2);
-    public static final LauncherState FAST_OVERVIEW = new FastOverviewState(3);
-    public static final LauncherState ALL_APPS = new AllAppsState(4);
+    public static final LauncherState SPRING_LOADED = new SpringLoadedState(
+            SPRING_LOADED_STATE_ORDINAL);
+    public static final LauncherState ALL_APPS = new AllAppsState(ALL_APPS_STATE_ORDINAL);
 
-    protected static final Rect sTempRect = new Rect();
+    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 QUICK_SWITCH =
+            OverviewState.newSwitchState(QUICK_SWITCH_STATE_ORDINAL);
+    public static final LauncherState BACKGROUND_APP =
+            OverviewState.newBackgroundState(BACKGROUND_APP_STATE_ORDINAL);
 
     public final int ordinal;
 
@@ -177,17 +200,21 @@
         return Arrays.copyOf(sAllStates, sAllStates.length);
     }
 
-    public float[] getWorkspaceScaleAndTranslation(Launcher launcher) {
-        return new float[] {1, 0, 0};
+    public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
+        return new ScaleAndTranslation(1, 0, 0);
     }
 
-    /**
-     * Returns 2 floats designating how to transition overview:
-     *   scale for the current and adjacent pages
-     *   translationY factor where 0 is top aligned and 0.5 is centered vertically
-     */
-    public float[] getOverviewScaleAndTranslationYFactor(Launcher launcher) {
-        return new float[] {1.1f, 0f};
+    public ScaleAndTranslation getHotseatScaleAndTranslation(Launcher launcher) {
+        // For most states, treat the hotseat as if it were part of the workspace.
+        return getWorkspaceScaleAndTranslation(launcher);
+    }
+
+    public ScaleAndTranslation getOverviewScaleAndTranslation(Launcher launcher) {
+        return UiFactory.getOverviewScaleAndTranslationForNormalState(launcher);
+    }
+
+    public float getOverviewFullscreenProgress() {
+        return 0;
     }
 
     public void onStateEnabled(Launcher launcher) {
@@ -242,15 +269,60 @@
      * Called when the start transition ends and the user settles on this particular state.
      */
     public void onStateTransitionEnd(Launcher launcher) {
-        if (this == NORMAL || this == SPRING_LOADED) {
-            UiFactory.resetOverview(launcher);
-        }
         if (this == NORMAL) {
             // Clear any rotation locks when going to normal state
             launcher.getRotationHelper().setCurrentStateRequest(REQUEST_NONE);
         }
     }
 
+    public void onBackPressed(Launcher launcher) {
+        if (this != NORMAL) {
+            LauncherStateManager lsm = launcher.getStateManager();
+            LauncherState lastState = lsm.getLastState();
+            lsm.goToState(lastState);
+        }
+    }
+
+    /**
+     * Prepares for a non-user controlled animation from fromState to this state. Preparations
+     * include:
+     * - Setting interpolators for various animations included in the state transition.
+     * - Setting some start values (e.g. scale) for views that are hidden but about to be shown.
+     */
+    public void prepareForAtomicAnimation(Launcher launcher, LauncherState fromState,
+            AnimatorSetBuilder builder) {
+        if (this == NORMAL && fromState == OVERVIEW) {
+            builder.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL);
+            builder.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL);
+            builder.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCEL, 0, 0.9f));
+            builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL);
+            builder.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7);
+            Workspace workspace = launcher.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 = launcher.getHotseat();
+            boolean isHotseatVisible = hotseat.getVisibility() == VISIBLE && hotseat.getAlpha() > 0;
+            if (!isHotseatVisible) {
+                hotseat.setScaleX(0.92f);
+                hotseat.setScaleY(0.92f);
+            }
+        } else if (this == NORMAL && fromState == OVERVIEW_PEEK) {
+            // Keep fully visible until the very end (when overview is offscreen) to make invisible.
+            builder.setInterpolator(ANIM_OVERVIEW_FADE, t -> t < 1 ? 0 : 1);
+        }
+    }
+
     protected static void dispatchWindowStateChanged(Launcher launcher) {
         launcher.getWindow().getDecorView().sendAccessibilityEvent(TYPE_WINDOW_STATE_CHANGED);
     }
@@ -265,4 +337,16 @@
 
         public abstract float getPageAlpha(int pageIndex);
     }
+
+    public static class ScaleAndTranslation {
+        public float scale;
+        public float translationX;
+        public float translationY;
+
+        public ScaleAndTranslation(float scale, float translationX, float translationY) {
+            this.scale = scale;
+            this.translationX = translationX;
+            this.translationY = translationY;
+        }
+    }
 }
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index 3c7c1aa..2c8c208 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -16,17 +16,7 @@
 
 package com.android.launcher3;
 
-import static android.view.View.VISIBLE;
 import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
-import static com.android.launcher3.anim.Interpolators.ACCEL;
-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.OVERSHOOT_1_2;
-import static com.android.launcher3.anim.Interpolators.clampToProgress;
 import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
 
 import android.animation.Animator;
@@ -34,19 +24,24 @@
 import android.animation.AnimatorSet;
 import android.os.Handler;
 import android.os.Looper;
-import android.support.annotation.IntDef;
+import android.util.Log;
 
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.AnimatorSetBuilder;
 import com.android.launcher3.anim.PropertySetter;
 import com.android.launcher3.anim.PropertySetter.AnimatedPropertySetter;
+import com.android.launcher3.compat.AccessibilityManagerCompat;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.uioverrides.UiFactory;
 
+import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 
+import androidx.annotation.IntDef;
+
 /**
  * TODO: figure out what kind of tests we can write for this
  *
@@ -95,23 +90,30 @@
     // 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.
+    // non atomic components as well. Note that each gesture model has exactly one atomic component,
+    // ATOMIC_OVERVIEW_SCALE_COMPONENT *or* ATOMIC_OVERVIEW_PEEK_COMPONENT.
     @IntDef(flag = true, value = {
             NON_ATOMIC_COMPONENT,
-            ATOMIC_COMPONENT
+            ATOMIC_OVERVIEW_SCALE_COMPONENT,
+            ATOMIC_OVERVIEW_PEEK_COMPONENT,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface AnimationComponents {}
     public static final int NON_ATOMIC_COMPONENT = 1 << 0;
-    public static final int ATOMIC_COMPONENT = 1 << 1;
+    public static final int ATOMIC_OVERVIEW_SCALE_COMPONENT = 1 << 1;
+    public static final int ATOMIC_OVERVIEW_PEEK_COMPONENT = 1 << 2;
 
-    public static final int ANIM_ALL = NON_ATOMIC_COMPONENT | ATOMIC_COMPONENT;
+    public static final int ANIM_ALL = NON_ATOMIC_COMPONENT | ATOMIC_OVERVIEW_SCALE_COMPONENT
+            | ATOMIC_OVERVIEW_PEEK_COMPONENT;
 
     private final AnimationConfig mConfig = new AnimationConfig();
     private final Handler mUiHandler;
     private final Launcher mLauncher;
     private final ArrayList<StateListener> mListeners = new ArrayList<>();
 
+    // Animators which are run on properties also controlled by state animations.
+    private Animator[] mStateElementAnimators;
+
     private StateHandler[] mStateHandlers;
     private LauncherState mState = NORMAL;
 
@@ -129,6 +131,19 @@
         return mState;
     }
 
+    public LauncherState getCurrentStableState() {
+        return mCurrentStableState;
+    }
+
+    public void dump(String prefix, PrintWriter writer) {
+        writer.println(prefix + "LauncherState");
+        writer.println(prefix + "\tmLastStableState:" + mLastStableState);
+        writer.println(prefix + "\tmCurrentStableState:" + mCurrentStableState);
+        writer.println(prefix + "\tmState:" + mState);
+        writer.println(prefix + "\tmRestState:" + mRestState);
+        writer.println(prefix + "\tisInTransition:" + (mConfig.mCurrentAnimation != null));
+    }
+
     public StateHandler[] getStateHandlers() {
         if (mStateHandlers == null) {
             mStateHandlers = UiFactory.getStateHandler(mLauncher);
@@ -145,10 +160,17 @@
     }
 
     /**
+     * Returns true if the state changes should be animated.
+     */
+    public boolean shouldAnimateStateChange() {
+        return !mLauncher.isForceInvisible() && mLauncher.isStarted();
+    }
+
+    /**
      * @see #goToState(LauncherState, boolean, Runnable)
      */
     public void goToState(LauncherState state) {
-        goToState(state, !mLauncher.isForceInvisible() && mLauncher.isStarted() /* animated */);
+        goToState(state, shouldAnimateStateChange());
     }
 
     /**
@@ -188,18 +210,24 @@
     }
 
     public void reapplyState(boolean cancelCurrentAnimation) {
+        boolean wasInAnimation = mConfig.mCurrentAnimation != null;
         if (cancelCurrentAnimation) {
+            cancelAllStateElementAnimation();
             cancelAnimation();
         }
         if (mConfig.mCurrentAnimation == null) {
             for (StateHandler handler : getStateHandlers()) {
                 handler.setState(mState);
             }
+            if (wasInAnimation) {
+                onStateTransitionEnd(mState);
+            }
         }
     }
 
     private void goToState(LauncherState state, boolean animated, long delay,
             final Runnable onCompleteRunnable) {
+        animated &= Utilities.areAnimationsEnabled(mLauncher);
         if (mLauncher.isInState(state)) {
             if (mConfig.mCurrentAnimation == null) {
                 // Run any queued runnable
@@ -226,14 +254,12 @@
         mConfig.reset();
 
         if (!animated) {
+            cancelAllStateElementAnimation();
             onStateTransitionStart(state);
             for (StateHandler handler : getStateHandlers()) {
                 handler.setState(state);
             }
 
-            for (int i = mListeners.size() - 1; i >= 0; i--) {
-                mListeners.get(i).onStateSetImmediately(state);
-            }
             onStateTransitionEnd(state);
 
             // Run any queued runnable
@@ -243,6 +269,22 @@
             return;
         }
 
+        if (delay > 0) {
+            // Create the animation after the delay as some properties can change between preparing
+            // the animation and running the animation.
+            int startChangeId = mConfig.mChangeId;
+            mUiHandler.postDelayed(() -> {
+                if (mConfig.mChangeId == startChangeId) {
+                    goToStateAnimated(state, fromState, onCompleteRunnable);
+                }
+            }, delay);
+        } else {
+            goToStateAnimated(state, fromState, onCompleteRunnable);
+        }
+    }
+
+    private void goToStateAnimated(LauncherState state, LauncherState fromState,
+            Runnable onCompleteRunnable) {
         // Since state NORMAL 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 == NORMAL ? fromState.transitionDuration : state.transitionDuration;
@@ -251,12 +293,7 @@
         prepareForAtomicAnimation(fromState, state, builder);
         AnimatorSet animation = createAnimationToNewWorkspaceInternal(
                 state, builder, onCompleteRunnable);
-        Runnable runnable = new StartAnimRunnable(animation);
-        if (delay > 0) {
-            mUiHandler.postDelayed(runnable, delay);
-        } else {
-            mUiHandler.post(runnable);
-        }
+        mUiHandler.post(new StartAnimRunnable(animation));
     }
 
     /**
@@ -266,34 +303,19 @@
      */
     public void prepareForAtomicAnimation(LauncherState fromState, LauncherState toState,
             AnimatorSetBuilder builder) {
-        if (fromState == NORMAL && toState.overviewUi) {
-            builder.setInterpolator(ANIM_WORKSPACE_SCALE, OVERSHOOT_1_2);
-            builder.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2);
-            builder.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2);
-            builder.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2);
+        toState.prepareForAtomicAnimation(mLauncher, fromState, builder);
+    }
 
-            // Start from a higher overview scale, but only if we're invisible so we don't jump.
-            UiFactory.prepareToShowOverview(mLauncher);
-        } else if (fromState.overviewUi && toState == NORMAL) {
-            builder.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL);
-            builder.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL);
-            builder.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCEL, 0, 0.9f));
-            builder.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7);
-            Workspace workspace = mLauncher.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);
-            }
+    public AnimatorSet createAtomicAnimation(LauncherState fromState, LauncherState toState,
+            AnimatorSetBuilder builder, @AnimationComponents int atomicComponent, long duration) {
+        prepareForAtomicAnimation(fromState, toState, builder);
+        AnimationConfig config = new AnimationConfig();
+        config.animComponents = atomicComponent;
+        config.duration = duration;
+        for (StateHandler handler : mLauncher.getStateManager().getStateHandlers()) {
+            handler.setStateWithAnimation(toState, builder, config);
         }
+        return builder.build();
     }
 
     /**
@@ -306,7 +328,13 @@
      */
     public AnimatorPlaybackController createAnimationToNewWorkspace(
             LauncherState fromState, LauncherState state, long duration) {
+        // Since we are creating a state animation to a different state, temporarily prevent state
+        // change as part of config reset.
+        LauncherState originalRestState = mRestState;
+        mRestState = state;
         mConfig.reset();
+        mRestState = originalRestState;
+
         for (StateHandler handler : getStateHandlers()) {
             handler.setState(fromState);
         }
@@ -349,7 +377,6 @@
             AnimatorSetBuilder builder, final Runnable onCompleteRunnable) {
 
         for (StateHandler handler : getStateHandlers()) {
-            builder.startTag(handler);
             handler.setStateWithAnimation(state, builder, mConfig);
         }
 
@@ -360,9 +387,6 @@
             public void onAnimationStart(Animator animation) {
                 // Change the internal state only when the transition actually starts
                 onStateTransitionStart(state);
-                for (int i = mListeners.size() - 1; i >= 0; i--) {
-                    mListeners.get(i).onStateTransitionStart(state);
-                }
             }
 
             @Override
@@ -372,9 +396,6 @@
                     onCompleteRunnable.run();
                 }
                 onStateTransitionEnd(state);
-                for (int i = mListeners.size() - 1; i >= 0; i--) {
-                    mListeners.get(i).onStateTransitionComplete(state);
-                }
             }
         });
         mConfig.setAnimation(animation, state);
@@ -382,24 +403,37 @@
     }
 
     private void onStateTransitionStart(LauncherState state) {
+        if (TestProtocol.sDebugTracing) {
+            android.util.Log.d(TestProtocol.NO_DRAG_TAG,
+                    "onStateTransitionStart");
+        }
         if (mState != state) {
             mState.onStateDisabled(mLauncher);
         }
         mState = state;
         mState.onStateEnabled(mLauncher);
-        mLauncher.getAppWidgetHost().setResumed(state == LauncherState.NORMAL);
+        mLauncher.onStateSet(mState);
 
         if (state.disablePageClipping) {
             // Only disable clipping if needed, otherwise leave it as previous value.
             mLauncher.getWorkspace().setClipChildren(false);
         }
         UiFactory.onLauncherStateOrResumeChanged(mLauncher);
+
+        for (int i = mListeners.size() - 1; i >= 0; i--) {
+            mListeners.get(i).onStateTransitionStart(state);
+        }
     }
 
     private void onStateTransitionEnd(LauncherState state) {
         // Only change the stable states after the transitions have finished
         if (state != mCurrentStableState) {
             mLastStableState = state.getHistoryForState(mCurrentStableState);
+            if (TestProtocol.sDebugTracing) {
+                Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG,
+                        "mCurrentStableState = " + state.getClass().getSimpleName() + " @ " +
+                                android.util.Log.getStackTraceString(new Throwable()));
+            }
             mCurrentStableState = state;
         }
 
@@ -413,7 +447,11 @@
 
         UiFactory.onLauncherStateOrResumeChanged(mLauncher);
 
-        mLauncher.getDragLayer().requestFocus();
+        for (int i = mListeners.size() - 1; i >= 0; i--) {
+            mListeners.get(i).onStateTransitionComplete(state);
+        }
+
+        AccessibilityManagerCompat.sendStateEventToTest(mLauncher, state.ordinal);
     }
 
     public void onWindowFocusChanged() {
@@ -482,10 +520,53 @@
         cancelAnimation();
         if (reapplyNeeded) {
             reapplyState();
+            // Dispatch on transition end, so that any transient property is cleared.
+            onStateTransitionEnd(mState);
         }
         mConfig.setAnimation(anim, null);
     }
 
+    private void cancelAllStateElementAnimation() {
+        if (mStateElementAnimators == null) {
+            return;
+        }
+
+        for (Animator animator : mStateElementAnimators) {
+            if (animator != null) {
+                animator.cancel();
+            }
+        }
+    }
+
+    /**
+     * Cancels a currently running gesture animation
+     */
+    public void cancelStateElementAnimation(int index) {
+        if (mStateElementAnimators == null) {
+            return;
+        }
+        if (mStateElementAnimators[index] != null) {
+            mStateElementAnimators[index].cancel();
+        }
+    }
+
+    public Animator createStateElementAnimation(int index, float... values) {
+        cancelStateElementAnimation(index);
+        LauncherAppTransitionManager latm = mLauncher.getAppTransitionManager();
+        if (mStateElementAnimators == null) {
+            mStateElementAnimators = new Animator[latm.getStateElementAnimationsCount()];
+        }
+        Animator anim = latm.createStateElementAnimation(index, values);
+        mStateElementAnimators[index] = anim;
+        anim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mStateElementAnimators[index] = null;
+            }
+        });
+        return anim;
+    }
+
     private void clearCurrentAnimation() {
         if (mConfig.mCurrentAnimation != null) {
             mConfig.mCurrentAnimation.removeListener(mConfig);
@@ -499,6 +580,10 @@
         private final AnimatorSet mAnim;
 
         public StartAnimRunnable(AnimatorSet anim) {
+            if (TestProtocol.sDebugTracing) {
+                android.util.Log.d(TestProtocol.NO_DRAG_TAG,
+                        "StartAnimRunnable");
+            }
             mAnim = anim;
         }
 
@@ -520,6 +605,8 @@
 
         private AnimatorSet mCurrentAnimation;
         private LauncherState mTargetState;
+        // Id to keep track of config changes, to tie an animation with the corresponding request
+        private int mChangeId = 0;
 
         /**
          * Cancels the current animation and resets config variables.
@@ -541,6 +628,7 @@
 
             mCurrentAnimation = null;
             playbackController = null;
+            mChangeId ++;
         }
 
         public PropertySetter getPropertySetter(AnimatorSetBuilder builder) {
@@ -567,8 +655,12 @@
             mCurrentAnimation.addListener(this);
         }
 
-        public boolean playAtomicComponent() {
-            return (animComponents & ATOMIC_COMPONENT) != 0;
+        public boolean playAtomicOverviewScaleComponent() {
+            return (animComponents & ATOMIC_OVERVIEW_SCALE_COMPONENT) != 0;
+        }
+
+        public boolean playAtomicOverviewPeekComponent() {
+            return (animComponents & ATOMIC_OVERVIEW_PEEK_COMPONENT) != 0;
         }
 
         public boolean playNonAtomicComponent() {
@@ -592,11 +684,6 @@
 
     public interface StateListener {
 
-        /**
-         * Called when the state is set without an animation.
-         */
-        void onStateSetImmediately(LauncherState state);
-
         void onStateTransitionStart(LauncherState toState);
         void onStateTransitionComplete(LauncherState finalState);
     }
diff --git a/src/com/android/launcher3/MainProcessInitializer.java b/src/com/android/launcher3/MainProcessInitializer.java
index 462eadb..95ee687 100644
--- a/src/com/android/launcher3/MainProcessInitializer.java
+++ b/src/com/android/launcher3/MainProcessInitializer.java
@@ -18,23 +18,26 @@
 
 import android.content.Context;
 
-import com.android.launcher3.graphics.IconShapeOverride;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.graphics.IconShape;
 import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.util.ResourceBasedOverride;
 
 /**
  * Utility class to handle one time initializations of the main process
  */
-public class MainProcessInitializer {
+public class MainProcessInitializer implements ResourceBasedOverride {
 
     public static void initialize(Context context) {
-        Utilities.getOverrideObject(
+        Overrides.getObject(
                 MainProcessInitializer.class, context, R.string.main_process_initializer_class)
                 .init(context);
     }
 
     protected void init(Context context) {
         FileLog.setDir(context.getApplicationContext().getFilesDir());
-        IconShapeOverride.apply(context);
+        FeatureFlags.initialize(context);
         SessionCommitReceiver.applyDefaultUserPrefs(context);
+        IconShape.init(context);
     }
 }
diff --git a/src/com/android/launcher3/OverviewButtonClickListener.java b/src/com/android/launcher3/OverviewButtonClickListener.java
deleted file mode 100644
index dd670d2..0000000
--- a/src/com/android/launcher3/OverviewButtonClickListener.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package com.android.launcher3;
-
-import android.view.View;
-
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-
-/**
- * A specialized listener for Overview buttons where both clicks and long clicks are logged
- * handled the same via {@link #handleViewClick(View)}.
- */
-public abstract class OverviewButtonClickListener implements View.OnClickListener,
-        View.OnLongClickListener {
-
-    private int mControlType; /** ControlType enum as defined in {@link Action.Touch} */
-
-    public OverviewButtonClickListener(int controlType) {
-        mControlType = controlType;
-    }
-
-    public void attachTo(View v) {
-        v.setOnClickListener(this);
-        v.setOnLongClickListener(this);
-    }
-
-    @Override
-    public void onClick(View view) {
-        if (shouldPerformClick(view)) {
-            handleViewClick(view, Action.Touch.TAP);
-        }
-    }
-
-    @Override
-    public boolean onLongClick(View view) {
-        if (shouldPerformClick(view)) {
-            handleViewClick(view, Action.Touch.LONGPRESS);
-        }
-        return true;
-    }
-
-    private boolean shouldPerformClick(View view) {
-        return !Launcher.getLauncher(view.getContext()).getWorkspace().isSwitchingState();
-    }
-
-    private void handleViewClick(View view, int action) {
-        handleViewClick(view);
-        Launcher.getLauncher(view.getContext()).getUserEventDispatcher()
-                .logActionOnControl(action, mControlType);
-    }
-
-    public abstract void handleViewClick(View view);
-}
\ No newline at end of file
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index db5dc66..2eeb132 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -16,14 +16,18 @@
 
 package com.android.launcher3;
 
+import static com.android.launcher3.Utilities.shouldDisableGestures;
 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 android.animation.LayoutTransition;
 import android.animation.TimeInterpolator;
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.res.TypedArray;
+import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.provider.Settings;
@@ -47,6 +51,7 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.pageindicators.PageIndicator;
 import com.android.launcher3.touch.OverScroll;
+import com.android.launcher3.util.OverScroller;
 import com.android.launcher3.util.Thunk;
 
 import java.util.ArrayList;
@@ -63,7 +68,6 @@
     protected static final ComputePageScrollsLogic SIMPLE_SCROLL_LOGIC = (v) -> v.getVisibility() != GONE;
 
     public static final int PAGE_SNAP_ANIMATION_DURATION = 750;
-    public static final int SLOW_PAGE_SNAP_ANIMATION_DURATION = 950;
 
     // OverScroll constants
     private final static int OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION = 270;
@@ -83,7 +87,6 @@
     public static final int INVALID_RESTORE_PAGE = -1001;
 
     private boolean mFreeScroll = false;
-    private boolean mSettleOnPageInFreeScroll = false;
 
     protected int mFlingThresholdVelocity;
     protected int mMinFlingVelocity;
@@ -96,8 +99,9 @@
 
     @ViewDebug.ExportedProperty(category = "launcher")
     protected int mNextPage = INVALID_PAGE;
+    protected int mMinScrollX;
     protected int mMaxScrollX;
-    protected LauncherScroller mScroller;
+    protected OverScroller mScroller;
     private Interpolator mDefaultInterpolator;
     private VelocityTracker mVelocityTracker;
     protected int mPageSpacing = 0;
@@ -109,13 +113,7 @@
     private float mTotalMotionX;
 
     protected int[] mPageScrolls;
-
-    protected final static int TOUCH_STATE_REST = 0;
-    protected final static int TOUCH_STATE_SCROLLING = 1;
-    protected final static int TOUCH_STATE_PREV_PAGE = 2;
-    protected final static int TOUCH_STATE_NEXT_PAGE = 3;
-
-    protected int mTouchState = TOUCH_STATE_REST;
+    private boolean mIsBeingDragged;
 
     protected int mTouchSlop;
     private int mMaximumVelocity;
@@ -127,12 +125,9 @@
 
     protected boolean mIsPageInTransition = false;
 
-    protected boolean mWasInOverscroll = false;
+    protected float mSpringOverScrollX;
 
-    // mOverScrollX is equal to getScrollX() when we're within the normal scroll range. Otherwise
-    // it is equal to the scaled overscroll position. We use a separate value so as to prevent
-    // the screens from continuing to translate beyond the normal bounds.
-    protected int mOverScrollX;
+    protected boolean mWasInOverscroll = false;
 
     protected int mUnboundedScrollX;
 
@@ -140,9 +135,6 @@
     @Thunk int mPageIndicatorViewId;
     protected T mPageIndicator;
 
-    // Convenience/caching
-    private static final Rect sTmpRect = new Rect();
-
     protected final Rect mInsets = new Rect();
     protected boolean mIsRtl;
 
@@ -176,7 +168,7 @@
      * Initializes various states for this workspace.
      */
     protected void init() {
-        mScroller = new LauncherScroller(getContext());
+        mScroller = new OverScroller(getContext());
         setDefaultInterpolator(Interpolators.SCROLL);
         mCurrentPage = 0;
 
@@ -251,7 +243,7 @@
             newX = getScrollForPage(mCurrentPage);
         }
         scrollTo(newX, 0);
-        mScroller.setFinalX(newX);
+        mScroller.startScroll(mScroller.getCurrPos(), newX - mScroller.getCurrPos());
         forceFinishScroller(true);
     }
 
@@ -276,14 +268,47 @@
     }
 
     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);
     }
 
     /**
+     * @return The closest page to the provided page that is within mMinScrollX and mMaxScrollX.
+     */
+    private int ensureWithinScrollBounds(int page) {
+        int dir = !mIsRtl ? 1 : - 1;
+        int currScroll = getScrollForPage(page);
+        int prevScroll;
+        while (currScroll < mMinScrollX) {
+            page += dir;
+            prevScroll = currScroll;
+            currScroll = getScrollForPage(page);
+            if (currScroll <= prevScroll) {
+                Log.e(TAG, "validateNewPage: failed to find a page > mMinScrollX");
+                break;
+            }
+        }
+        while (currScroll > mMaxScrollX) {
+            page -= dir;
+            prevScroll = currScroll;
+            currScroll = getScrollForPage(page);
+            if (currScroll >= prevScroll) {
+                Log.e(TAG, "validateNewPage: failed to find a page < mMaxScrollX");
+                break;
+            }
+        }
+        return page;
+    }
+
+    public void setCurrentPage(int currentPage) {
+        setCurrentPage(currentPage, INVALID_PAGE);
+    }
+
+    /**
      * Sets the current page.
      */
-    public void setCurrentPage(int currentPage) {
+    public void setCurrentPage(int currentPage, int overridePrevPage) {
         if (!mScroller.isFinished()) {
             abortScrollerAnimation(true);
         }
@@ -292,7 +317,7 @@
         if (getChildCount() == 0) {
             return;
         }
-        int prevPage = mCurrentPage;
+        int prevPage = overridePrevPage != INVALID_PAGE ? overridePrevPage : mCurrentPage;
         mCurrentPage = validateNewPage(currentPage);
         updateCurrentPageScroll();
         notifyPageSwitchListener(prevPage);
@@ -356,37 +381,31 @@
 
     @Override
     public void scrollTo(int x, int y) {
-        // In free scroll mode, we clamp the scrollX
-        if (mFreeScroll) {
-            // If the scroller is trying to move to a location beyond the maximum allowed
-            // in the free scroll mode, we make sure to end the scroll operation.
-            if (!mScroller.isFinished() && (x > mMaxScrollX || x < 0)) {
-                forceFinishScroller(false);
-            }
-
-            x = Utilities.boundToRange(x, 0, mMaxScrollX);
-        }
-
         mUnboundedScrollX = x;
 
-        boolean isXBeforeFirstPage = mIsRtl ? (x > mMaxScrollX) : (x < 0);
-        boolean isXAfterLastPage = mIsRtl ? (x < 0) : (x > mMaxScrollX);
+        boolean isXBeforeFirstPage = mIsRtl ? (x > mMaxScrollX) : (x < mMinScrollX);
+        boolean isXAfterLastPage = mIsRtl ? (x < mMinScrollX) : (x > mMaxScrollX);
+
+        if (!isXBeforeFirstPage && !isXAfterLastPage) {
+            mSpringOverScrollX = 0;
+        }
+
         if (isXBeforeFirstPage) {
-            super.scrollTo(mIsRtl ? mMaxScrollX : 0, y);
+            super.scrollTo(mIsRtl ? mMaxScrollX : mMinScrollX, y);
             if (mAllowOverScroll) {
                 mWasInOverscroll = true;
                 if (mIsRtl) {
                     overScroll(x - mMaxScrollX);
                 } else {
-                    overScroll(x);
+                    overScroll(x - mMinScrollX);
                 }
             }
         } else if (isXAfterLastPage) {
-            super.scrollTo(mIsRtl ? 0 : mMaxScrollX, y);
+            super.scrollTo(mIsRtl ? mMinScrollX : mMaxScrollX, y);
             if (mAllowOverScroll) {
                 mWasInOverscroll = true;
                 if (mIsRtl) {
-                    overScroll(x);
+                    overScroll(x - mMinScrollX);
                 } else {
                     overScroll(x - mMaxScrollX);
                 }
@@ -396,9 +415,9 @@
                 overScroll(0);
                 mWasInOverscroll = false;
             }
-            mOverScrollX = x;
             super.scrollTo(x, y);
         }
+
     }
 
     private void sendScrollAccessibilityEvent() {
@@ -432,10 +451,9 @@
     protected boolean computeScrollHelper(boolean shouldInvalidate) {
         if (mScroller.computeScrollOffset()) {
             // Don't bother scrolling if the page does not need to be moved
-            if (getUnboundedScrollX() != mScroller.getCurrX()
-                    || getScrollY() != mScroller.getCurrY()
-                    || mOverScrollX != mScroller.getCurrX()) {
-                scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
+            if (getUnboundedScrollX() != mScroller.getCurrPos()
+                    || getScrollX() != mScroller.getCurrPos()) {
+                scrollTo(mScroller.getCurrPos(), 0);
             }
             if (shouldInvalidate) {
                 invalidate();
@@ -451,7 +469,7 @@
 
             // We don't want to trigger a page end moving unless the page has settled
             // and the user has stopped scrolling
-            if (mTouchState == TOUCH_STATE_REST) {
+            if (!mIsBeingDragged) {
                 pageEndTransition();
             }
 
@@ -574,12 +592,12 @@
                     // Wait until all transitions are complete.
                     if (!transition.isRunning()) {
                         transition.removeTransitionListener(this);
-                        updateMaxScrollX();
+                        updateMinAndMaxScrollX();
                     }
                 }
             });
         } else {
-            updateMaxScrollX();
+            updateMinAndMaxScrollX();
         }
 
         if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < childCount) {
@@ -644,10 +662,15 @@
         return 0;
     }
 
-    private void updateMaxScrollX() {
+    protected void updateMinAndMaxScrollX() {
+        mMinScrollX = computeMinScrollX();
         mMaxScrollX = computeMaxScrollX();
     }
 
+    protected int computeMinScrollX() {
+        return 0;
+    }
+
     protected int computeMaxScrollX() {
         int childCount = getChildCount();
         if (childCount > 0) {
@@ -663,6 +686,10 @@
         requestLayout();
     }
 
+    public int getPageSpacing() {
+        return mPageSpacing;
+    }
+
     private void dispatchPageCountChanged() {
         if (mPageIndicator != null) {
             mPageIndicator.setMarkersCount(getChildCount());
@@ -811,12 +838,6 @@
         super.requestDisallowInterceptTouchEvent(disallowIntercept);
     }
 
-    /** Returns whether x and y originated within the buffered viewport */
-    private boolean isTouchPointInViewportWithBuffer(int x, int y) {
-        sTmpRect.set(-getMeasuredWidth() / 2, 0, 3 * getMeasuredWidth() / 2, getMeasuredHeight());
-        return sTmpRect.contains(x, y);
-    }
-
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         /*
@@ -824,10 +845,11 @@
          * If we return true, onTouchEvent will be called and we do the actual
          * scrolling there.
          */
-        acquireVelocityTrackerAndAddMovement(ev);
 
         // Skip touch handling if there are no pages to swipe
-        if (getChildCount() <= 0) return super.onInterceptTouchEvent(ev);
+        if (getChildCount() <= 0 || shouldDisableGestures(ev)) return false;
+
+        acquireVelocityTrackerAndAddMovement(ev);
 
         /*
          * Shortcut the most recurring case: the user is in the dragging
@@ -835,8 +857,7 @@
          * motion.
          */
         final int action = ev.getAction();
-        if ((action == MotionEvent.ACTION_MOVE) &&
-                (mTouchState == TOUCH_STATE_SCROLLING)) {
+        if ((action == MotionEvent.ACTION_MOVE) && mIsBeingDragged) {
             return true;
         }
 
@@ -873,21 +894,17 @@
                  * otherwise don't.  mScroller.isFinished should be false when
                  * being flinged.
                  */
-                final int xDist = Math.abs(mScroller.getFinalX() - mScroller.getCurrX());
+                final int xDist = Math.abs(mScroller.getFinalPos() - mScroller.getCurrPos());
                 final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop / 3);
 
                 if (finishedScrolling) {
-                    mTouchState = TOUCH_STATE_REST;
+                    mIsBeingDragged = false;
                     if (!mScroller.isFinished() && !mFreeScroll) {
                         setCurrentPage(getNextPage());
                         pageEndTransition();
                     }
                 } else {
-                    if (isTouchPointInViewportWithBuffer((int) mDownMotionX, (int) mDownMotionY)) {
-                        mTouchState = TOUCH_STATE_SCROLLING;
-                    } else {
-                        mTouchState = TOUCH_STATE_REST;
-                    }
+                    mIsBeingDragged = true;
                 }
 
                 break;
@@ -908,11 +925,11 @@
          * The only time we want to intercept motion events is if we are in the
          * drag mode.
          */
-        return mTouchState != TOUCH_STATE_REST;
+        return mIsBeingDragged;
     }
 
     public boolean isHandlingTouch() {
-        return mTouchState != TOUCH_STATE_REST;
+        return mIsBeingDragged;
     }
 
     protected void determineScrollingStart(MotionEvent ev) {
@@ -928,19 +945,14 @@
         final int pointerIndex = ev.findPointerIndex(mActivePointerId);
         if (pointerIndex == -1) return;
 
-        // Disallow scrolling if we started the gesture from outside the viewport
         final float x = ev.getX(pointerIndex);
-        final float y = ev.getY(pointerIndex);
-        if (!isTouchPointInViewportWithBuffer((int) x, (int) y)) return;
-
         final int xDiff = (int) Math.abs(x - mLastMotionX);
-
         final int touchSlop = Math.round(touchSlopScale * mTouchSlop);
         boolean xMoved = xDiff > touchSlop;
 
         if (xMoved) {
             // Scroll if the user moved far enough along the X axis
-            mTouchState = TOUCH_STATE_SCROLLING;
+            mIsBeingDragged = true;
             mTotalMotionX += Math.abs(mLastMotionX - x);
             mLastMotionX = x;
             mLastMotionXRemainder = 0;
@@ -1008,31 +1020,64 @@
         }
     }
 
-    protected void dampedOverScroll(float amount) {
-        if (Float.compare(amount, 0f) == 0) return;
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        if (mScroller.isSpringing() && mSpringOverScrollX != 0) {
+            int saveCount = canvas.save();
+
+            canvas.translate(-mSpringOverScrollX, 0);
+            super.dispatchDraw(canvas);
+
+            canvas.restoreToCount(saveCount);
+        } else {
+            super.dispatchDraw(canvas);
+        }
+    }
+
+    protected void dampedOverScroll(int amount) {
+        mSpringOverScrollX = amount;
+        if (amount == 0) {
+            return;
+        }
 
         int overScrollAmount = OverScroll.dampedScroll(amount, getMeasuredWidth());
-        if (amount < 0) {
-            mOverScrollX = overScrollAmount;
-            super.scrollTo(mOverScrollX, getScrollY());
-        } else {
-            mOverScrollX = mMaxScrollX + overScrollAmount;
-            super.scrollTo(mOverScrollX, getScrollY());
+        mSpringOverScrollX = overScrollAmount;
+        if (mScroller.isSpringing()) {
+            invalidate();
+            return;
         }
+
+        int x = Utilities.boundToRange(getScrollX(), mMinScrollX, mMaxScrollX);
+        super.scrollTo(x + overScrollAmount, getScrollY());
         invalidate();
     }
 
-    protected void overScroll(float amount) {
-        dampedOverScroll(amount);
+    protected void overScroll(int amount) {
+        mSpringOverScrollX = amount;
+        if (mScroller.isSpringing()) {
+            invalidate();
+            return;
+        }
+
+        if (amount == 0) return;
+
+        if (mFreeScroll && !mScroller.isFinished()) {
+            if (amount < 0) {
+                super.scrollTo(mMinScrollX + amount, getScrollY());
+            } else {
+                super.scrollTo(mMaxScrollX + amount, getScrollY());
+            }
+        } else {
+            dampedOverScroll(amount);
+        }
     }
 
 
-    protected void enableFreeScroll(boolean settleOnPageInFreeScroll) {
-        setEnableFreeScroll(true);
-        mSettleOnPageInFreeScroll = settleOnPageInFreeScroll;
-    }
+    public void setEnableFreeScroll(boolean freeScroll) {
+        if (mFreeScroll == freeScroll) {
+            return;
+        }
 
-    private void setEnableFreeScroll(boolean freeScroll) {
         boolean wasFreeScroll = mFreeScroll;
         mFreeScroll = freeScroll;
 
@@ -1041,8 +1086,6 @@
         } else if (wasFreeScroll) {
             snapToPage(getNextPage());
         }
-
-        setEnableOverscroll(!freeScroll);
     }
 
     protected void setEnableOverscroll(boolean enable) {
@@ -1051,10 +1094,8 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
-        super.onTouchEvent(ev);
-
         // Skip touch handling if there are no pages to swipe
-        if (getChildCount() <= 0) return super.onTouchEvent(ev);
+        if (getChildCount() <= 0 || shouldDisableGestures(ev)) return false;
 
         acquireVelocityTrackerAndAddMovement(ev);
 
@@ -1077,14 +1118,14 @@
             mTotalMotionX = 0;
             mActivePointerId = ev.getPointerId(0);
 
-            if (mTouchState == TOUCH_STATE_SCROLLING) {
+            if (mIsBeingDragged) {
                 onScrollInteractionBegin();
                 pageBeginTransition();
             }
             break;
 
         case MotionEvent.ACTION_MOVE:
-            if (mTouchState == TOUCH_STATE_SCROLLING) {
+            if (mIsBeingDragged) {
                 // Scroll to follow the motion event
                 final int pointerIndex = ev.findPointerIndex(mActivePointerId);
 
@@ -1111,13 +1152,13 @@
             break;
 
         case MotionEvent.ACTION_UP:
-            if (mTouchState == TOUCH_STATE_SCROLLING) {
+            if (mIsBeingDragged) {
                 final int activePointerId = mActivePointerId;
                 final int pointerIndex = ev.findPointerIndex(activePointerId);
                 final float x = ev.getX(pointerIndex);
                 final VelocityTracker velocityTracker = mVelocityTracker;
                 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
-                int velocityX = (int) velocityTracker.getXVelocity(activePointerId);
+                int velocityX = (int) velocityTracker.getXVelocity(mActivePointerId);
                 final int deltaX = (int) (x - mDownMotionX);
                 final int pageWidth = getPageAt(mCurrentPage).getMeasuredWidth();
                 boolean isSignificantMove = Math.abs(deltaX) > pageWidth *
@@ -1125,6 +1166,8 @@
 
                 mTotalMotionX += Math.abs(mLastMotionX + mLastMotionXRemainder - x);
                 boolean isFling = mTotalMotionX > mTouchSlop && shouldFlingForVelocity(velocityX);
+                boolean isDeltaXLeft = mIsRtl ? deltaX > 0 : deltaX < 0;
+                boolean isVelocityXLeft = mIsRtl ? velocityX > 0 : velocityX < 0;
 
                 if (!mFreeScroll) {
                     // In the case that the page is moved far to one direction and then is flung
@@ -1140,8 +1183,7 @@
                     // We give flings precedence over large moves, which is why we short-circuit our
                     // test for a large move if a fling has been registered. That is, a large
                     // move to the left and fling to the right will register as a fling to the right.
-                    boolean isDeltaXLeft = mIsRtl ? deltaX > 0 : deltaX < 0;
-                    boolean isVelocityXLeft = mIsRtl ? velocityX > 0 : velocityX < 0;
+
                     if (((isSignificantMove && !isDeltaXLeft && !isFling) ||
                             (isFling && !isVelocityXLeft)) && mCurrentPage > 0) {
                         finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage - 1;
@@ -1159,60 +1201,46 @@
                         abortScrollerAnimation(true);
                     }
 
-                    float scaleX = getScaleX();
-                    int vX = (int) (-velocityX * scaleX);
-                    int initialScrollX = (int) (getScrollX() * scaleX);
+                    int initialScrollX = getScrollX();
 
-                    mScroller.setInterpolator(mDefaultInterpolator);
-                    mScroller.fling(initialScrollX,
-                            getScrollY(), vX, 0, Integer.MIN_VALUE, Integer.MAX_VALUE, 0, 0);
-                    int unscaledScrollX = (int) (mScroller.getFinalX() / scaleX);
-                    mNextPage = getPageNearestToCenterOfScreen(unscaledScrollX);
-                    int firstPageScroll = getScrollForPage(!mIsRtl ? 0 : getPageCount() - 1);
-                    int lastPageScroll = getScrollForPage(!mIsRtl ? getPageCount() - 1 : 0);
-                    if (mSettleOnPageInFreeScroll && unscaledScrollX > 0
-                            && unscaledScrollX < mMaxScrollX) {
-                        // 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.
-                        final int finalX = unscaledScrollX < firstPageScroll / 2 ?
-                                0 :
-                                unscaledScrollX > (lastPageScroll + mMaxScrollX) / 2 ?
-                                        mMaxScrollX :
-                                        getScrollForPage(mNextPage);
+                    if (((initialScrollX >= mMaxScrollX) && (isVelocityXLeft || !isFling)) ||
+                            ((initialScrollX <= mMinScrollX) && (!isVelocityXLeft || !isFling))) {
+                        mScroller.springBack(getScrollX(), mMinScrollX, mMaxScrollX);
+                        mNextPage = getPageNearestToCenterOfScreen();
+                    } else {
+                        mScroller.setInterpolator(mDefaultInterpolator);
+                        mScroller.fling(initialScrollX, -velocityX,
+                                mMinScrollX, mMaxScrollX,
+                                Math.round(getWidth() * 0.5f * OVERSCROLL_DAMP_FACTOR));
 
-                        mScroller.setFinalX((int) (finalX * getScaleX()));
-                        // 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 finalX = mScroller.getFinalPos();
+                        mNextPage = getPageNearestToCenterOfScreen(finalX);
+
+                        int firstPageScroll = getScrollForPage(!mIsRtl ? 0 : getPageCount() - 1);
+                        int lastPageScroll = getScrollForPage(!mIsRtl ? getPageCount() - 1 : 0);
+                        if (finalX > mMinScrollX && finalX < mMaxScrollX) {
+                            // 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 pageSnappedX = finalX < (firstPageScroll + mMinScrollX) / 2
+                                    ? mMinScrollX
+                                    : finalX > (lastPageScroll + mMaxScrollX) / 2
+                                            ? mMaxScrollX
+                                            : getScrollForPage(mNextPage);
+
+                            mScroller.setFinalPos(pageSnappedX);
+                            // Ensure the scroll/snap doesn't happen too fast;
+                            int extraScrollDuration = OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION
+                                    - mScroller.getDuration();
+                            if (extraScrollDuration > 0) {
+                                mScroller.extendDuration(extraScrollDuration);
+                            }
                         }
                     }
                     invalidate();
                 }
                 onScrollInteractionEnd();
-            } else if (mTouchState == TOUCH_STATE_PREV_PAGE) {
-                // at this point we have not moved beyond the touch slop
-                // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so
-                // we can just page
-                int nextPage = Math.max(0, mCurrentPage - 1);
-                if (nextPage != mCurrentPage) {
-                    snapToPage(nextPage);
-                } else {
-                    snapToDestination();
-                }
-            } else if (mTouchState == TOUCH_STATE_NEXT_PAGE) {
-                // at this point we have not moved beyond the touch slop
-                // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so
-                // we can just page
-                int nextPage = Math.min(getChildCount() - 1, mCurrentPage + 1);
-                if (nextPage != mCurrentPage) {
-                    snapToPage(nextPage);
-                } else {
-                    snapToDestination();
-                }
             }
 
             // End any intermediate reordering states
@@ -1220,7 +1248,7 @@
             break;
 
         case MotionEvent.ACTION_CANCEL:
-            if (mTouchState == TOUCH_STATE_SCROLLING) {
+            if (mIsBeingDragged) {
                 snapToDestination();
                 onScrollInteractionEnd();
             }
@@ -1242,7 +1270,7 @@
 
     private void resetTouchState() {
         releaseVelocityTracker();
-        mTouchState = TOUCH_STATE_REST;
+        mIsBeingDragged = false;
         mActivePointerId = INVALID_POINTER;
     }
 
@@ -1356,7 +1384,7 @@
     }
 
     protected boolean isInOverScroll() {
-        return (mOverScrollX > mMaxScrollX || mOverScrollX < 0);
+        return (getScrollX() > mMaxScrollX || getScrollX() < mMinScrollX);
     }
 
     protected int getPageSnapDuration() {
@@ -1406,7 +1434,12 @@
         // interpolator at zero, ie. 5. We use 4 to make it a little slower.
         duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
 
-        return snapToPage(whichPage, delta, duration);
+        if (QUICKSTEP_SPRINGS.get()) {
+            return snapToPage(whichPage, delta, duration, false, null,
+                    velocity * Math.signum(newX - getUnboundedScrollX()), true);
+        } else {
+            return snapToPage(whichPage, delta, duration);
+        }
     }
 
     public boolean snapToPage(int whichPage) {
@@ -1431,23 +1464,23 @@
 
         int newX = getScrollForPage(whichPage);
         final int delta = newX - getUnboundedScrollX();
-        return snapToPage(whichPage, delta, duration, immediate, interpolator);
+        return snapToPage(whichPage, delta, duration, immediate, interpolator, 0, false);
     }
 
     protected boolean snapToPage(int whichPage, int delta, int duration) {
-        return snapToPage(whichPage, delta, duration, false, null);
+        return snapToPage(whichPage, delta, duration, false, null, 0, false);
     }
 
     protected boolean snapToPage(int whichPage, int delta, int duration, boolean immediate,
-            TimeInterpolator interpolator) {
+            TimeInterpolator interpolator, float velocity, boolean spring) {
         if (mFirstLayout) {
             setCurrentPage(whichPage);
             return false;
         }
 
         if (FeatureFlags.IS_DOGFOOD_BUILD) {
-            duration *= Settings.System.getFloat(getContext().getContentResolver(),
-                    Settings.System.WINDOW_ANIMATION_SCALE, 1);
+            duration *= Settings.Global.getFloat(getContext().getContentResolver(),
+                    Settings.Global.WINDOW_ANIMATION_SCALE, 1);
         }
 
         whichPage = validateNewPage(whichPage);
@@ -1475,7 +1508,11 @@
             mScroller.setInterpolator(mDefaultInterpolator);
         }
 
-        mScroller.startScroll(getUnboundedScrollX(), 0, delta, 0, duration);
+        if (spring && QUICKSTEP_SPRINGS.get()) {
+            mScroller.startScrollSpring(getUnboundedScrollX(), delta, duration, velocity);
+        } else {
+            mScroller.startScroll(getUnboundedScrollX(), delta, duration);
+        }
 
         updatePageIndicator();
 
diff --git a/src/com/android/launcher3/PromiseAppInfo.java b/src/com/android/launcher3/PromiseAppInfo.java
index ea9f752..4ad0b3d 100644
--- a/src/com/android/launcher3/PromiseAppInfo.java
+++ b/src/com/android/launcher3/PromiseAppInfo.java
@@ -18,11 +18,12 @@
 
 import android.content.Context;
 import android.content.Intent;
-import android.support.annotation.NonNull;
 
 import com.android.launcher3.compat.PackageInstallerCompat;
 import com.android.launcher3.util.PackageManagerHelper;
 
+import androidx.annotation.NonNull;
+
 public class PromiseAppInfo extends AppInfo {
 
     public int level = 0;
@@ -37,13 +38,13 @@
     }
 
     @Override
-    public ShortcutInfo makeShortcut() {
-        ShortcutInfo shortcut = new ShortcutInfo(this);
+    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 |= ShortcutInfo.FLAG_AUTOINSTALL_ICON;
+        shortcut.status |= WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON;
         // Since the user is manually placing it on homescreen, it should not be auto-removed later
-        shortcut.status |= ShortcutInfo.FLAG_RESTORE_STARTED;
+        shortcut.status |= WorkspaceItemInfo.FLAG_RESTORE_STARTED;
         return shortcut;
     }
 
diff --git a/src/com/android/launcher3/ResourceUtils.java b/src/com/android/launcher3/ResourceUtils.java
new file mode 100644
index 0000000..73e705b
--- /dev/null
+++ b/src/com/android/launcher3/ResourceUtils.java
@@ -0,0 +1,46 @@
+/*
+ * 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;
+
+import android.content.res.Resources;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+
+public class ResourceUtils {
+    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 int getNavbarSize(String resName, Resources res) {
+        return getDimenByName(resName, res, 48);
+    }
+
+    private static int getDimenByName(String resName, Resources res, int defaultValue) {
+        final int frameSize;
+        final int frameSizeResID = res.getIdentifier(resName, "dimen", "android");
+        if (frameSizeResID != 0) {
+            frameSize = res.getDimensionPixelSize(frameSizeResID);
+        } else {
+            frameSize = pxFromDp(defaultValue, res.getDisplayMetrics());
+        }
+        return frameSize;
+    }
+
+    public static int pxFromDp(float size, DisplayMetrics metrics) {
+        return Math.round(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 76e85e2..55cb6f2 100644
--- a/src/com/android/launcher3/SecondaryDropTarget.java
+++ b/src/com/android/launcher3/SecondaryDropTarget.java
@@ -2,7 +2,6 @@
 
 import static android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID;
 import static android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE;
-
 import static com.android.launcher3.ItemInfoWithIcon.FLAG_SYSTEM_MASK;
 import static com.android.launcher3.ItemInfoWithIcon.FLAG_SYSTEM_NO;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
@@ -10,7 +9,6 @@
 import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.UNINSTALL;
 
 import android.appwidget.AppWidgetHostView;
-import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
 import android.content.Context;
@@ -152,7 +150,7 @@
         Intent intent = null;
         UserHandle user = null;
         if (item != null &&
-                item.itemType == LauncherSettings.BaseLauncherColumns.ITEM_TYPE_APPLICATION) {
+                item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
             intent = item.getIntent();
             user = item.user;
         }
@@ -181,7 +179,7 @@
             DeferredOnComplete deferred = (DeferredOnComplete) d.dragSource;
             if (target != null) {
                 deferred.mPackageName = target.getPackageName();
-                mLauncher.setOnResumeCallback(deferred);
+                mLauncher.addOnResumeCallback(deferred);
             } else {
                 deferred.sendFailure();
             }
diff --git a/src/com/android/launcher3/SettingsActivity.java b/src/com/android/launcher3/SettingsActivity.java
deleted file mode 100644
index 32c198a..0000000
--- a/src/com/android/launcher3/SettingsActivity.java
+++ /dev/null
@@ -1,300 +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 static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY;
-import static com.android.launcher3.states.RotationHelper.getAllowRotationDefaultValue;
-
-import android.annotation.TargetApi;
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.app.FragmentManager;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.os.Build;
-import android.os.Bundle;
-import android.preference.ListPreference;
-import android.preference.Preference;
-import android.preference.PreferenceFragment;
-import android.preference.PreferenceScreen;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.view.View;
-import android.widget.Adapter;
-import android.widget.ListView;
-
-import com.android.launcher3.graphics.IconShapeOverride;
-import com.android.launcher3.notification.NotificationListener;
-import com.android.launcher3.util.ListViewHighlighter;
-import com.android.launcher3.util.SettingsObserver;
-import com.android.launcher3.views.ButtonPreference;
-
-import java.util.Objects;
-
-/**
- * Settings activity for Launcher. Currently implements the following setting: Allow rotation
- */
-public class SettingsActivity extends Activity {
-
-    private static final String ICON_BADGING_PREFERENCE_KEY = "pref_icon_badging";
-    /** Hidden field Settings.Secure.NOTIFICATION_BADGING */
-    public static final String NOTIFICATION_BADGING = "notification_badging";
-    /** Hidden field Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */
-    private static final String NOTIFICATION_ENABLED_LISTENERS = "enabled_notification_listeners";
-
-    private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
-    private static final String EXTRA_SHOW_FRAGMENT_ARGS = ":settings:show_fragment_args";
-    private static final int DELAY_HIGHLIGHT_DURATION_MILLIS = 600;
-    private static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted";
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        if (savedInstanceState == null) {
-            // Display the fragment as the main content.
-            getFragmentManager().beginTransaction()
-                    .replace(android.R.id.content, getNewFragment())
-                    .commit();
-        }
-    }
-
-    protected PreferenceFragment getNewFragment() {
-        return new LauncherSettingsFragment();
-    }
-
-    /**
-     * This fragment shows the launcher preferences.
-     */
-    public static class LauncherSettingsFragment extends PreferenceFragment {
-
-        private IconBadgingObserver mIconBadgingObserver;
-
-        private String mPreferenceKey;
-        private boolean mPreferenceHighlighted = false;
-
-        @Override
-        public void onCreate(Bundle savedInstanceState) {
-            super.onCreate(savedInstanceState);
-            if (savedInstanceState != null) {
-                mPreferenceHighlighted = savedInstanceState.getBoolean(SAVE_HIGHLIGHTED_KEY);
-            }
-
-            getPreferenceManager().setSharedPreferencesName(LauncherFiles.SHARED_PREFERENCES_KEY);
-            addPreferencesFromResource(R.xml.launcher_preferences);
-
-            ContentResolver resolver = getActivity().getContentResolver();
-
-            ButtonPreference iconBadgingPref =
-                    (ButtonPreference) findPreference(ICON_BADGING_PREFERENCE_KEY);
-            if (!Utilities.ATLEAST_OREO) {
-                getPreferenceScreen().removePreference(
-                        findPreference(SessionCommitReceiver.ADD_ICON_PREFERENCE_KEY));
-                getPreferenceScreen().removePreference(iconBadgingPref);
-            } else if (!getResources().getBoolean(R.bool.notification_badging_enabled)) {
-                getPreferenceScreen().removePreference(iconBadgingPref);
-            } else {
-                // Listen to system notification badge settings while this UI is active.
-                mIconBadgingObserver = new IconBadgingObserver(
-                        iconBadgingPref, resolver, getFragmentManager());
-                mIconBadgingObserver.register(NOTIFICATION_BADGING, NOTIFICATION_ENABLED_LISTENERS);
-            }
-
-            Preference iconShapeOverride = findPreference(IconShapeOverride.KEY_PREFERENCE);
-            if (iconShapeOverride != null) {
-                if (IconShapeOverride.isSupported(getActivity())) {
-                    IconShapeOverride.handlePreferenceUi((ListPreference) iconShapeOverride);
-                } else {
-                    getPreferenceScreen().removePreference(iconShapeOverride);
-                }
-            }
-
-            // Setup allow rotation preference
-            Preference rotationPref = findPreference(ALLOW_ROTATION_PREFERENCE_KEY);
-            if (getResources().getBoolean(R.bool.allow_rotation)) {
-                // Launcher supports rotation by default. No need to show this setting.
-                getPreferenceScreen().removePreference(rotationPref);
-            } else {
-                // Initialize the UI once
-                rotationPref.setDefaultValue(getAllowRotationDefaultValue());
-            }
-        }
-
-        @Override
-        public void onSaveInstanceState(Bundle outState) {
-            super.onSaveInstanceState(outState);
-            outState.putBoolean(SAVE_HIGHLIGHTED_KEY, mPreferenceHighlighted);
-        }
-
-        @Override
-        public void onResume() {
-            super.onResume();
-
-            Intent intent = getActivity().getIntent();
-            mPreferenceKey = intent.getStringExtra(EXTRA_FRAGMENT_ARG_KEY);
-            if (isAdded() && !mPreferenceHighlighted && !TextUtils.isEmpty(mPreferenceKey)) {
-                getView().postDelayed(this::highlightPreference, DELAY_HIGHLIGHT_DURATION_MILLIS);
-            }
-        }
-
-        private void highlightPreference() {
-            Preference pref = findPreference(mPreferenceKey);
-            if (pref == null || getPreferenceScreen() == null) {
-                return;
-            }
-            PreferenceScreen screen = getPreferenceScreen();
-            if (Utilities.ATLEAST_OREO) {
-                screen = selectPreferenceRecursive(pref, screen);
-            }
-            if (screen == null) {
-                return;
-            }
-
-            View root = screen.getDialog() != null
-                    ? screen.getDialog().getWindow().getDecorView() : getView();
-            ListView list = root.findViewById(android.R.id.list);
-            if (list == null || list.getAdapter() == null) {
-                return;
-            }
-            Adapter adapter = list.getAdapter();
-
-            // Find the position
-            int position = -1;
-            for (int i = adapter.getCount() - 1; i >= 0; i--) {
-                if (pref == adapter.getItem(i)) {
-                    position = i;
-                    break;
-                }
-            }
-            new ListViewHighlighter(list, position);
-            mPreferenceHighlighted = true;
-        }
-
-        @Override
-        public void onDestroy() {
-            if (mIconBadgingObserver != null) {
-                mIconBadgingObserver.unregister();
-                mIconBadgingObserver = null;
-            }
-            super.onDestroy();
-        }
-
-        @TargetApi(Build.VERSION_CODES.O)
-        private PreferenceScreen selectPreferenceRecursive(
-                Preference pref, PreferenceScreen topParent) {
-            if (!(pref.getParent() instanceof PreferenceScreen)) {
-                return null;
-            }
-
-            PreferenceScreen parent = (PreferenceScreen) pref.getParent();
-            if (Objects.equals(parent.getKey(), topParent.getKey())) {
-                return parent;
-            } else if (selectPreferenceRecursive(parent, topParent) != null) {
-                ((PreferenceScreen) parent.getParent())
-                        .onItemClick(null, null, parent.getOrder(), 0);
-                return parent;
-            } else {
-                return null;
-            }
-        }
-    }
-
-    /**
-     * Content observer which listens for system badging setting changes,
-     * and updates the launcher badging setting subtext accordingly.
-     */
-    private static class IconBadgingObserver extends SettingsObserver.Secure
-            implements Preference.OnPreferenceClickListener {
-
-        private final ButtonPreference mBadgingPref;
-        private final ContentResolver mResolver;
-        private final FragmentManager mFragmentManager;
-
-        public IconBadgingObserver(ButtonPreference badgingPref, ContentResolver resolver,
-                FragmentManager fragmentManager) {
-            super(resolver);
-            mBadgingPref = badgingPref;
-            mResolver = resolver;
-            mFragmentManager = fragmentManager;
-        }
-
-        @Override
-        public void onSettingChanged(boolean enabled) {
-            int summary = enabled ? R.string.icon_badging_desc_on : R.string.icon_badging_desc_off;
-
-            boolean serviceEnabled = true;
-            if (enabled) {
-                // Check if the listener is enabled or not.
-                String enabledListeners =
-                        Settings.Secure.getString(mResolver, NOTIFICATION_ENABLED_LISTENERS);
-                ComponentName myListener =
-                        new ComponentName(mBadgingPref.getContext(), NotificationListener.class);
-                serviceEnabled = enabledListeners != null &&
-                        (enabledListeners.contains(myListener.flattenToString()) ||
-                                enabledListeners.contains(myListener.flattenToShortString()));
-                if (!serviceEnabled) {
-                    summary = R.string.title_missing_notification_access;
-                }
-            }
-            mBadgingPref.setWidgetFrameVisible(!serviceEnabled);
-            mBadgingPref.setOnPreferenceClickListener(serviceEnabled ? null : this);
-            mBadgingPref.setSummary(summary);
-
-        }
-
-        @Override
-        public boolean onPreferenceClick(Preference preference) {
-            new NotificationAccessConfirmation().show(mFragmentManager, "notification_access");
-            return true;
-        }
-    }
-
-    public static class NotificationAccessConfirmation
-            extends DialogFragment implements DialogInterface.OnClickListener {
-
-        @Override
-        public Dialog onCreateDialog(Bundle savedInstanceState) {
-            final Context context = getActivity();
-            String msg = context.getString(R.string.msg_missing_notification_access,
-                    context.getString(R.string.derived_app_name));
-            return new AlertDialog.Builder(context)
-                    .setTitle(R.string.title_missing_notification_access)
-                    .setMessage(msg)
-                    .setNegativeButton(android.R.string.cancel, null)
-                    .setPositiveButton(R.string.title_change_settings, this)
-                    .create();
-        }
-
-        @Override
-        public void onClick(DialogInterface dialogInterface, int i) {
-            ComponentName cn = new ComponentName(getActivity(), NotificationListener.class);
-            Bundle showFragmentArgs = new Bundle();
-            showFragmentArgs.putString(EXTRA_FRAGMENT_ARG_KEY, cn.flattenToString());
-
-            Intent intent = new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS)
-                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-                    .putExtra(EXTRA_FRAGMENT_ARG_KEY, cn.flattenToString())
-                    .putExtra(EXTRA_SHOW_FRAGMENT_ARGS, showFragmentArgs);
-            getActivity().startActivity(intent);
-        }
-    }
-}
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index baf6d87..1bd8263 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -26,6 +26,7 @@
 import android.view.ViewGroup;
 
 import com.android.launcher3.CellLayout.ContainerType;
+import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
 
 public class ShortcutAndWidgetContainer extends ViewGroup {
@@ -43,12 +44,12 @@
 
     private int mCountX;
 
-    private Launcher mLauncher;
+    private ActivityContext mActivity;
     private boolean mInvertIfRtl = false;
 
     public ShortcutAndWidgetContainer(Context context, @ContainerType int containerType) {
         super(context);
-        mLauncher = Launcher.getLauncher(context);
+        mActivity = ActivityContext.lookupContext(context);
         mWallpaperManager = WallpaperManager.getInstance(context);
         mContainerType = containerType;
     }
@@ -92,7 +93,7 @@
     public void setupLp(View child) {
         CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
         if (child instanceof LauncherAppWidgetHostView) {
-            DeviceProfile profile = mLauncher.getDeviceProfile();
+            DeviceProfile profile = mActivity.getWallpaperDeviceProfile();
             lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX,
                     profile.appWidgetScale.x, profile.appWidgetScale.y);
         } else {
@@ -107,12 +108,12 @@
 
     public int getCellContentHeight() {
         return Math.min(getMeasuredHeight(),
-                mLauncher.getDeviceProfile().getCellHeight(mContainerType));
+                mActivity.getWallpaperDeviceProfile().getCellHeight(mContainerType));
     }
 
     public void measureChild(View child) {
         CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
-        final DeviceProfile profile = mLauncher.getDeviceProfile();
+        final DeviceProfile profile = mActivity.getWallpaperDeviceProfile();
 
         if (child instanceof LauncherAppWidgetHostView) {
             lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX,
@@ -149,7 +150,7 @@
                     LauncherAppWidgetHostView lahv = (LauncherAppWidgetHostView) child;
 
                     // Scale and center the widget to fit within its cells.
-                    DeviceProfile profile = mLauncher.getDeviceProfile();
+                    DeviceProfile profile = mActivity.getDeviceProfile();
                     float scaleX = profile.appWidgetScale.x;
                     float scaleY = profile.appWidgetScale.y;
 
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
deleted file mode 100644
index 8588c7a..0000000
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ /dev/null
@@ -1,203 +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 android.annotation.TargetApi;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Build;
-import android.text.TextUtils;
-
-import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
-import com.android.launcher3.util.ContentWriter;
-
-/**
- * Represents a launchable icon on the workspaces and in folders.
- */
-public class ShortcutInfo extends ItemInfoWithIcon {
-
-    public static final int DEFAULT = 0;
-
-    /**
-     * The shortcut was restored from a backup and it not ready to be used. This is automatically
-     * set during backup/restore
-     */
-    public static final int FLAG_RESTORED_ICON = 1;
-
-    /**
-     * The icon was added as an auto-install app, and is not ready to be used. This flag can't
-     * be present along with {@link #FLAG_RESTORED_ICON}, and is set during default layout
-     * parsing.
-     */
-    public static final int FLAG_AUTOINSTALL_ICON = 2; //0B10;
-
-    /**
-     * 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 = 4; // 0B100;
-
-    /**
-     * Indicates that the widget restore has started.
-     */
-    public static final int FLAG_RESTORE_STARTED = 8; //0B1000;
-
-    /**
-     * Web UI supported.
-     */
-    public static final int FLAG_SUPPORTS_WEB_UI = 16; //0B10000;
-
-    /**
-     * The intent used to start the application.
-     */
-    public Intent intent;
-
-    /**
-     * If isShortcut=true and customIcon=false, this contains a reference to the
-     * shortcut icon as an application's resource.
-     */
-    public Intent.ShortcutIconResource iconResource;
-
-    /**
-     * A message to display when the user tries to start a disabled shortcut.
-     * This is currently only used for deep shortcuts.
-     */
-    public CharSequence disabledMessage;
-
-    public int status;
-
-    /**
-     * The installation progress [0-100] of the package that this shortcut represents.
-     */
-    private int mInstallProgress;
-
-    public ShortcutInfo() {
-        itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT;
-    }
-
-    public ShortcutInfo(ShortcutInfo info) {
-        super(info);
-        title = info.title;
-        intent = new Intent(info.intent);
-        iconResource = info.iconResource;
-        status = info.status;
-        mInstallProgress = info.mInstallProgress;
-    }
-
-    /** TODO: Remove this.  It's only called by ApplicationInfo.makeShortcut. */
-    public ShortcutInfo(AppInfo info) {
-        super(info);
-        title = Utilities.trim(info.title);
-        intent = new Intent(info.intent);
-    }
-
-    /**
-     * Creates a {@link ShortcutInfo} from a {@link ShortcutInfoCompat}.
-     */
-    @TargetApi(Build.VERSION_CODES.N)
-    public ShortcutInfo(ShortcutInfoCompat shortcutInfo, Context context) {
-        user = shortcutInfo.getUserHandle();
-        itemType = LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
-        updateFromDeepShortcutInfo(shortcutInfo, context);
-    }
-
-    @Override
-    public void onAddToDatabase(ContentWriter writer) {
-        super.onAddToDatabase(writer);
-        writer.put(LauncherSettings.BaseLauncherColumns.TITLE, title)
-                .put(LauncherSettings.BaseLauncherColumns.INTENT, getIntent())
-                .put(LauncherSettings.Favorites.RESTORED, status);
-
-        if (!usingLowResIcon) {
-            writer.putIcon(iconBitmap, user);
-        }
-        if (iconResource != null) {
-            writer.put(LauncherSettings.BaseLauncherColumns.ICON_PACKAGE, iconResource.packageName)
-                    .put(LauncherSettings.BaseLauncherColumns.ICON_RESOURCE,
-                            iconResource.resourceName);
-        }
-    }
-
-    @Override
-    public Intent getIntent() {
-        return intent;
-    }
-
-    public boolean hasStatusFlag(int flag) {
-        return (status & flag) != 0;
-    }
-
-
-    public final boolean isPromise() {
-        return hasStatusFlag(FLAG_RESTORED_ICON | FLAG_AUTOINSTALL_ICON);
-    }
-
-    public boolean hasPromiseIconUi() {
-        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(ShortcutInfoCompat shortcutInfo, Context context) {
-        // {@link ShortcutInfoCompat#getActivity} can change during an update. Recreate the intent
-        intent = shortcutInfo.makeIntent();
-        title = shortcutInfo.getShortLabel();
-
-        CharSequence label = shortcutInfo.getLongLabel();
-        if (TextUtils.isEmpty(label)) {
-            label = shortcutInfo.getShortLabel();
-        }
-        contentDescription = UserManagerCompat.getInstance(context)
-                .getBadgedLabelForUser(label, user);
-        if (shortcutInfo.isEnabled()) {
-            runtimeStatusFlags &= ~FLAG_DISABLED_BY_PUBLISHER;
-        } else {
-            runtimeStatusFlags |= FLAG_DISABLED_BY_PUBLISHER;
-        }
-        disabledMessage = shortcutInfo.getDisabledMessage();
-    }
-
-    /** Returns the ShortcutInfo id associated with the deep shortcut. */
-    public String getDeepShortcutId() {
-        return itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT ?
-                getIntent().getStringExtra(ShortcutInfoCompat.EXTRA_SHORTCUT_ID) : null;
-    }
-
-    @Override
-    public ComponentName getTargetComponent() {
-        ComponentName cn = super.getTargetComponent();
-        if (cn == null && (itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
-                || hasStatusFlag(FLAG_SUPPORTS_WEB_UI))) {
-            // 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
-            // check everywhere.
-            String pkg = intent.getPackage();
-            return pkg == null ? null : new ComponentName(pkg, IconCache.EMPTY_CLASS_NAME);
-        }
-        return cn;
-    }
-}
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 0e6d567..65aa3a7 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -16,22 +16,33 @@
 
 package com.android.launcher3;
 
+import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
+import android.app.ActivityManager;
 import android.app.WallpaperManager;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.LauncherActivityInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
+import android.content.pm.ShortcutInfo;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.Color;
 import android.graphics.Matrix;
 import android.graphics.Paint;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.InsetDrawable;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.DeadObjectException;
@@ -39,6 +50,7 @@
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.TransactionTooLargeException;
+import android.provider.Settings;
 import android.text.Spannable;
 import android.text.SpannableString;
 import android.text.TextUtils;
@@ -47,19 +59,32 @@
 import android.util.Log;
 import android.util.Pair;
 import android.util.TypedValue;
+import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewConfiguration;
 import android.view.animation.Interpolator;
 
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.ShortcutConfigActivityInfo;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
+import com.android.launcher3.graphics.RotationMode;
+import com.android.launcher3.graphics.TintedDrawableSpan;
+import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.shortcuts.DeepShortcutManager;
+import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.views.Transposable;
+import com.android.launcher3.widget.PendingAddShortcutInfo;
 
-import java.io.ByteArrayOutputStream;
 import java.io.Closeable;
 import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
-import java.util.Collection;
-import java.util.HashSet;
+import java.util.Arrays;
+import java.util.List;
 import java.util.Locale;
+import java.util.StringTokenizer;
 import java.util.concurrent.Executor;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.ThreadPoolExecutor;
@@ -67,6 +92,8 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_ICON_BADGED;
+
 /**
  * Various utilities shared amongst the Launcher's classes.
  */
@@ -79,10 +106,11 @@
 
     private static final int[] sLoc0 = new int[2];
     private static final int[] sLoc1 = new int[2];
-    private static final float[] sPoint = new float[2];
     private static final Matrix sMatrix = new Matrix();
     private static final Matrix sInverseMatrix = new Matrix();
 
+    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;
 
@@ -92,21 +120,24 @@
     public static final boolean ATLEAST_OREO =
             Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
 
-    public static final boolean ATLEAST_NOUGAT_MR1 =
-            Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1;
-
-    public static final boolean ATLEAST_NOUGAT =
-            Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
-
-    public static final boolean ATLEAST_MARSHMALLOW =
-            Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
-
-    public static final boolean ATLEAST_LOLLIPOP_MR1 =
-            Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1;
-
     public static final int SINGLE_FRAME_MS = 16;
 
     /**
+     * Set on a motion event dispatched from the nav bar. See {@link MotionEvent#setEdgeFlags(int)}.
+     */
+    public static final int EDGE_NAV_BAR = 1 << 8;
+
+    /**
+     * Set on a motion event do disallow any gestures and only handle touch.
+     * See {@link MotionEvent#setEdgeFlags(int)}.
+     */
+    public static final int FLAG_NO_GESTURES = 1 << 9;
+
+    public static boolean shouldDisableGestures(MotionEvent ev) {
+        return (ev.getEdgeFlags() & FLAG_NO_GESTURES) == FLAG_NO_GESTURES;
+    }
+
+    /**
      * Indicates if the device has a debug build. Should only be used to store additional info or
      * add extra logging and not for changing the app behavior.
      */
@@ -114,11 +145,14 @@
             Build.TYPE.toLowerCase(Locale.ROOT).contains("debug") ||
             Build.TYPE.toLowerCase(Locale.ROOT).equals("eng");
 
+    public static boolean isDevelopersOptionsEnabled(Context context) {
+        return Settings.Global.getInt(context.getApplicationContext().getContentResolver(),
+                        Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
+    }
+
     // An intent extra to indicate the horizontal scroll of the wallpaper.
     public static final String EXTRA_WALLPAPER_OFFSET = "com.android.launcher3.WALLPAPER_OFFSET";
-
-    public static final int COLOR_EXTRACTION_JOB_ID = 1;
-    public static final int WALLPAPER_COMPAT_JOB_ID = 2;
+    public static final String EXTRA_WALLPAPER_FLAVOR = "com.android.launcher3.WALLPAPER_FLAVOR";
 
     // These values are same as that in {@link AsyncTask}.
     private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
@@ -130,12 +164,25 @@
      */
     public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(
             CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
-            TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
+            TimeUnit.SECONDS, new LinkedBlockingQueue<>());
+
+    public static boolean IS_RUNNING_IN_TEST_HARNESS =
+                    ActivityManager.isRunningInTestHarness();
+
+    public static void enableRunningInTestHarnessForTests() {
+        IS_RUNNING_IN_TEST_HARNESS = true;
+    }
 
     public static boolean isPropertyEnabled(String propertyName) {
         return Log.isLoggable(propertyName, Log.VERBOSE);
     }
 
+    public static boolean existsStyleWallpapers(Context context) {
+        ResolveInfo ri = context.getPackageManager().resolveActivity(
+                PackageManagerHelper.getStyleWallpapersIntent(context), 0);
+        return ri != null;
+    }
+
     /**
      * Given a coordinate relative to the descendant, find the coordinate in a parent view's
      * coordinates.
@@ -150,37 +197,67 @@
      *         assumption fails, we will need to return a pair of scale factors.
      */
     public static float getDescendantCoordRelativeToAncestor(
-            View descendant, View ancestor, int[] coord, boolean includeRootScroll) {
-        sPoint[0] = coord[0];
-        sPoint[1] = coord[1];
+            View descendant, View ancestor, float[] coord, boolean includeRootScroll) {
+        return getDescendantCoordRelativeToAncestor(descendant, ancestor, coord, includeRootScroll,
+                false, null);
+    }
 
+    /**
+     * Given a coordinate relative to the descendant, find the coordinate in a parent view's
+     * coordinates.
+     *
+     * @param descendant The descendant to which the passed coordinate is relative.
+     * @param ancestor The root view to make the coordinates relative to.
+     * @param coord The coordinate that we want mapped.
+     * @param includeRootScroll Whether or not to account for the scroll of the descendant:
+     *          sometimes this is relevant as in a child's coordinates within the descendant.
+     * @param ignoreTransform If true, view transform is ignored
+     * @param outRotation If not null, and {@param ignoreTransform} is true, this is set to the
+     *                   overall rotation of the view in degrees.
+     * @return The factor by which this descendant is scaled relative to this DragLayer. Caution
+     *         this scale factor is assumed to be equal in X and Y, and so if at any point this
+     *         assumption fails, we will need to return a pair of scale factors.
+     */
+    public static float getDescendantCoordRelativeToAncestor(View descendant, View ancestor,
+            float[] coord, boolean includeRootScroll, boolean ignoreTransform,
+            float[] outRotation) {
         float scale = 1.0f;
         View v = descendant;
         while(v != ancestor && v != null) {
             // For TextViews, scroll has a meaning which relates to the text position
             // which is very strange... ignore the scroll.
             if (v != descendant || includeRootScroll) {
-                sPoint[0] -= v.getScrollX();
-                sPoint[1] -= v.getScrollY();
+                offsetPoints(coord, -v.getScrollX(), -v.getScrollY());
             }
 
-            v.getMatrix().mapPoints(sPoint);
-            sPoint[0] += v.getLeft();
-            sPoint[1] += v.getTop();
+            if (ignoreTransform) {
+                if (v instanceof Transposable) {
+                    RotationMode m = ((Transposable) v).getRotationMode();
+                    if (m.isTransposed) {
+                        sMatrix.setRotate(m.surfaceRotation, v.getPivotX(), v.getPivotY());
+                        sMatrix.mapPoints(coord);
+
+                        if (outRotation != null) {
+                            outRotation[0] += m.surfaceRotation;
+                        }
+                    }
+                }
+            } else {
+                v.getMatrix().mapPoints(coord);
+            }
+            offsetPoints(coord, v.getLeft(), v.getTop());
             scale *= v.getScaleX();
 
             v = (View) v.getParent();
         }
-
-        coord[0] = Math.round(sPoint[0]);
-        coord[1] = Math.round(sPoint[1]);
         return scale;
     }
 
+
     /**
-     * Inverse of {@link #getDescendantCoordRelativeToAncestor(View, View, int[], boolean)}.
+     * Inverse of {@link #getDescendantCoordRelativeToAncestor(View, View, float[], boolean)}.
      */
-    public static void mapCoordInSelfToDescendant(View descendant, View root, int[] coord) {
+    public static void mapCoordInSelfToDescendant(View descendant, View root, float[] coord) {
         sMatrix.reset();
         View v = descendant;
         while(v != root) {
@@ -191,12 +268,23 @@
         }
         sMatrix.postTranslate(-v.getScrollX(), -v.getScrollY());
         sMatrix.invert(sInverseMatrix);
+        sInverseMatrix.mapPoints(coord);
+    }
 
-        sPoint[0] = coord[0];
-        sPoint[1] = coord[1];
-        sInverseMatrix.mapPoints(sPoint);
-        coord[0] = Math.round(sPoint[0]);
-        coord[1] = Math.round(sPoint[1]);
+    /**
+     * Sets {@param out} to be same as {@param in} by rounding individual values
+     */
+    public static void roundArray(float[] in, int[] out) {
+       for (int i = 0; i < in.length; i++) {
+           out[i] = Math.round(in[i]);
+       }
+    }
+
+    public static void offsetPoints(float[] points, float offsetX, float offsetY) {
+        for (int i = 0; i < points.length; i += 2) {
+            points[i] += offsetX;
+            points[i + 1] += offsetY;
+        }
     }
 
     /**
@@ -289,10 +377,14 @@
             Log.e(TAG, "mapToRange: range has 0 length");
             return toMin;
         }
-        float progress = Math.abs(t - fromMin) / Math.abs(fromMax - fromMin);
+        float progress = getProgress(t, fromMin, fromMax);
         return mapRange(interpolator.getInterpolation(progress), toMin, toMax);
     }
 
+    public static float getProgress(float current, float min, float max) {
+        return Math.abs(current - min) / Math.abs(max - min);
+    }
+
     public static float mapRange(float value, float min, float max) {
         return min + (value * (max - min));
     }
@@ -345,25 +437,6 @@
     }
 
     /**
-     * Compresses the bitmap to a byte array for serialization.
-     */
-    public static byte[] flattenBitmap(Bitmap bitmap) {
-        // Try go guesstimate how much space the icon will take when serialized
-        // to avoid unnecessary allocations/copies during the write.
-        int size = bitmap.getWidth() * bitmap.getHeight() * 4;
-        ByteArrayOutputStream out = new ByteArrayOutputStream(size);
-        try {
-            bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
-            out.flush();
-            out.close();
-            return out.toByteArray();
-        } catch (IOException e) {
-            Log.w(TAG, "Could not write bitmap");
-            return null;
-        }
-    }
-
-    /**
      * Trims the string, removing all whitespace at the beginning and end of the string.
      * Non-breaking whitespaces are also removed.
      */
@@ -436,17 +509,14 @@
         float densityRatio = (float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
         return (size / densityRatio);
     }
-    public static int pxFromDp(float size, DisplayMetrics metrics) {
-        return (int) Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
-                size, metrics));
-    }
+
     public static int pxFromSp(float size, DisplayMetrics metrics) {
         return (int) Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
                 size, metrics));
     }
 
-    public static String createDbSelectionQuery(String columnName, Iterable<?> values) {
-        return String.format(Locale.ENGLISH, "%s IN (%s)", columnName, TextUtils.join(", ", values));
+    public static String createDbSelectionQuery(String columnName, IntArray values) {
+        return String.format(Locale.ENGLISH, "%s IN (%s)", columnName, values.toConcatString());
     }
 
     public static boolean isBootCompleted() {
@@ -504,10 +574,17 @@
     }
 
     /**
-     * Replacement for Long.compare() which was added in API level 19.
+     * Prefixes a text with the provided icon
      */
-    public static int longCompare(long lhs, long rhs) {
-        return lhs < rhs ? -1 : (lhs == rhs ? 0 : 1);
+    public static CharSequence prefixTextWithIcon(Context context, int iconRes, CharSequence msg) {
+        // Update the hint to contain the icon.
+        // Prefix the original hint with two spaces. The first space gets replaced by the icon
+        // using span. The second space is used for a singe space character between the hint
+        // and the icon.
+        SpannableString spanned = new SpannableString("  " + msg);
+        spanned.setSpan(new TintedDrawableSpan(context, iconRes),
+                0, 1, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
+        return spanned;
     }
 
     public static SharedPreferences getPrefs(Context context) {
@@ -520,24 +597,14 @@
                 LauncherFiles.DEVICE_PREFERENCES_KEY, Context.MODE_PRIVATE);
     }
 
-    public static boolean isPowerSaverPreventingAnimation(Context context) {
-        if (ATLEAST_P) {
-            // Battery saver mode no longer prevents animations.
-            return false;
-        }
-        PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
-        return powerManager.isPowerSaveMode();
+    public static boolean areAnimationsEnabled(Context context) {
+        return ATLEAST_OREO
+                ? ValueAnimator.areAnimatorsEnabled()
+                : !context.getSystemService(PowerManager.class).isPowerSaveMode();
     }
 
     public static boolean isWallpaperAllowed(Context context) {
-        if (ATLEAST_NOUGAT) {
-            try {
-                WallpaperManager wm = context.getSystemService(WallpaperManager.class);
-                return (Boolean) wm.getClass().getDeclaredMethod("isSetWallpaperAllowed")
-                        .invoke(wm);
-            } catch (Exception e) { }
-        }
-        return true;
+        return context.getSystemService(WallpaperManager.class).isSetWallpaperAllowed();
     }
 
     public static void closeSilently(Closeable c) {
@@ -552,65 +619,11 @@
         }
     }
 
-    /**
-     * Returns true if {@param original} contains all entries defined in {@param updates} and
-     * have the same value.
-     * The comparison uses {@link Object#equals(Object)} to compare the values.
-     */
-    public static boolean containsAll(Bundle original, Bundle updates) {
-        for (String key : updates.keySet()) {
-            Object value1 = updates.get(key);
-            Object value2 = original.get(key);
-            if (value1 == null) {
-                if (value2 != null) {
-                    return false;
-                }
-            } else if (!value1.equals(value2)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /** Returns whether the collection is null or empty. */
-    public static boolean isEmpty(Collection c) {
-        return c == null || c.isEmpty();
-    }
-
     public static boolean isBinderSizeError(Exception e) {
         return e.getCause() instanceof TransactionTooLargeException
                 || e.getCause() instanceof DeadObjectException;
     }
 
-    public static <T> T getOverrideObject(Class<T> clazz, Context context, int resId) {
-        String className = context.getString(resId);
-        if (!TextUtils.isEmpty(className)) {
-            try {
-                Class<?> cls = Class.forName(className);
-                return (T) cls.getDeclaredConstructor(Context.class).newInstance(context);
-            } catch (ClassNotFoundException | InstantiationException | IllegalAccessException
-                    | ClassCastException | NoSuchMethodException | InvocationTargetException e) {
-                Log.e(TAG, "Bad overriden class", e);
-            }
-        }
-
-        try {
-            return clazz.newInstance();
-        } catch (InstantiationException|IllegalAccessException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    /**
-     * Returns a HashSet with a single element. We use this instead of Collections.singleton()
-     * because HashSet ensures all operations, such as remove, are supported.
-     */
-    public static <T> HashSet<T> singletonHashSet(T elem) {
-        HashSet<T> hashSet = new HashSet<>(1);
-        hashSet.add(elem);
-        return hashSet;
-    }
-
     /**
      * Utility method to post a runnable on the handler, skipping the synchronization barriers.
      */
@@ -619,4 +632,152 @@
         msg.setAsynchronous(true);
         handler.sendMessage(msg);
     }
+
+    /**
+     * Parses a string encoded using {@link #getPointString(int, int)}
+     */
+    public static Point parsePoint(String point) {
+        String[] split = point.split(",");
+        return new Point(Integer.parseInt(split[0]), Integer.parseInt(split[1]));
+    }
+
+    /**
+     * Encodes a point to string to that it can be persisted atomically.
+     */
+    public static String getPointString(int x, int y) {
+        return String.format(Locale.ENGLISH, "%d,%d", x, y);
+    }
+
+    public static void unregisterReceiverSafely(Context context, BroadcastReceiver receiver) {
+        try {
+            context.unregisterReceiver(receiver);
+        } catch (IllegalArgumentException e) {}
+    }
+
+    /**
+     * Returns the full drawable for {@param info}.
+     * @param outObj this is set to the internal data associated with {@param info},
+     *               eg {@link LauncherActivityInfo} or {@link ShortcutInfo}.
+     */
+    public static Drawable getFullDrawable(Launcher launcher, ItemInfo info, int width, int height,
+            boolean flattenDrawable, Object[] outObj) {
+        LauncherAppState appState = LauncherAppState.getInstance(launcher);
+        if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
+            LauncherActivityInfo activityInfo = LauncherAppsCompat.getInstance(launcher)
+                    .resolveActivity(info.getIntent(), info.user);
+            outObj[0] = activityInfo;
+            return (activityInfo != null) ? appState.getIconCache()
+                    .getFullResIcon(activityInfo, flattenDrawable) : null;
+        } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+            if (info instanceof PendingAddShortcutInfo) {
+                ShortcutConfigActivityInfo activityInfo =
+                        ((PendingAddShortcutInfo) info).activityInfo;
+                outObj[0] = activityInfo;
+                return activityInfo.getFullResIcon(appState.getIconCache());
+            }
+            ShortcutKey key = ShortcutKey.fromItemInfo(info);
+            DeepShortcutManager sm = DeepShortcutManager.getInstance(launcher);
+            List<ShortcutInfo> si = sm.queryForFullDetails(
+                    key.componentName.getPackageName(), Arrays.asList(key.getId()), key.user);
+            if (si.isEmpty()) {
+                return null;
+            } else {
+                outObj[0] = si.get(0);
+                return sm.getShortcutIconDrawable(si.get(0),
+                        appState.getInvariantDeviceProfile().fillResIconDpi);
+            }
+        } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
+            FolderAdaptiveIcon icon = FolderAdaptiveIcon.createFolderAdaptiveIcon(
+                    launcher, info.id, new Point(width, height));
+            if (icon == null) {
+                return null;
+            }
+            outObj[0] = icon;
+            return icon;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * For apps icons and shortcut icons that have badges, this method creates a drawable that can
+     * later on be rendered on top of the layers for the badges. For app icons, work profile badges
+     * can only be applied. For deep shortcuts, when dragged from the pop up container, there's no
+     * badge. When dragged from workspace or folder, it may contain app AND/OR work profile badge
+     **/
+    @TargetApi(Build.VERSION_CODES.O)
+    public static Drawable getBadge(Launcher launcher, ItemInfo info, Object obj) {
+        LauncherAppState appState = LauncherAppState.getInstance(launcher);
+        int iconSize = appState.getInvariantDeviceProfile().iconBitmapSize;
+        if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+            boolean iconBadged = (info instanceof ItemInfoWithIcon)
+                    && (((ItemInfoWithIcon) info).runtimeStatusFlags & FLAG_ICON_BADGED) > 0;
+            if ((info.id == ItemInfo.NO_ID && !iconBadged)
+                    || !(obj instanceof ShortcutInfo)) {
+                // The item is not yet added on home screen.
+                return new FixedSizeEmptyDrawable(iconSize);
+            }
+            ShortcutInfo si = (ShortcutInfo) obj;
+            LauncherIcons li = LauncherIcons.obtain(appState.getContext());
+            Bitmap badge = li.getShortcutInfoBadge(si, appState.getIconCache()).iconBitmap;
+            li.recycle();
+            float badgeSize = launcher.getResources().getDimension(R.dimen.profile_badge_size);
+            float insetFraction = (iconSize - badgeSize) / iconSize;
+            return new InsetDrawable(new FastBitmapDrawable(badge),
+                    insetFraction, insetFraction, 0, 0);
+        } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
+            return ((FolderAdaptiveIcon) obj).getBadge();
+        } else {
+            return launcher.getPackageManager()
+                    .getUserBadgedIcon(new FixedSizeEmptyDrawable(iconSize), info.user);
+        }
+    }
+
+    public static int[] getIntArrayFromString(String tokenized) {
+        StringTokenizer tokenizer = new StringTokenizer(tokenized, ",");
+        int[] array = new int[tokenizer.countTokens()];
+        int count = 0;
+        while (tokenizer.hasMoreTokens()) {
+            array[count] = Integer.parseInt(tokenizer.nextToken());
+            count++;
+        }
+        return array;
+    }
+
+    public static String getStringFromIntArray(int[] array) {
+        StringBuilder str = new StringBuilder();
+        for (int value : array) {
+            str.append(value).append(",");
+        }
+        return str.toString();
+    }
+
+    public static float squaredHypot(float x, float y) {
+        return x * x + y * y;
+    }
+
+    public static float squaredTouchSlop(Context context) {
+        float slop = ViewConfiguration.get(context).getScaledTouchSlop();
+        return slop * slop;
+    }
+
+    private static class FixedSizeEmptyDrawable extends ColorDrawable {
+
+        private final int mSize;
+
+        public FixedSizeEmptyDrawable(int size) {
+            super(Color.TRANSPARENT);
+            mSize = size;
+        }
+
+        @Override
+        public int getIntrinsicHeight() {
+            return mSize;
+        }
+
+        @Override
+        public int getIntrinsicWidth() {
+            return mSize;
+        }
+    }
 }
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index 7af4bf9..6d1bc1a 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -24,16 +24,18 @@
 import android.os.AsyncTask;
 import android.os.CancellationSignal;
 import android.os.Handler;
+import android.os.Process;
 import android.os.UserHandle;
-import android.support.annotation.Nullable;
 import android.util.Log;
 import android.util.LongSparseArray;
 
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.ShortcutConfigActivityInfo;
 import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.graphics.LauncherIcons;
-import com.android.launcher3.graphics.ShadowGenerator;
+import com.android.launcher3.icons.GraphicsUtils;
+import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.icons.ShadowGenerator;
+import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.PackageUserKey;
@@ -51,6 +53,8 @@
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
 
+import androidx.annotation.Nullable;
+
 public class WidgetPreviewLoader {
 
     private static final String TAG = "WidgetPreviewLoader";
@@ -70,7 +74,6 @@
     private final Context mContext;
     private final IconCache mIconCache;
     private final UserManagerCompat mUserManager;
-    private final AppWidgetManagerCompat mWidgetManager;
     private final CacheDb mDb;
 
     private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
@@ -79,7 +82,6 @@
     public WidgetPreviewLoader(Context context, IconCache iconCache) {
         mContext = context;
         mIconCache = iconCache;
-        mWidgetManager = AppWidgetManagerCompat.getInstance(context);
         mUserManager = UserManagerCompat.getInstance(context);
         mDb = new CacheDb(context);
         mWorkerHandler = new Handler(LauncherModel.getWorkerLooper());
@@ -104,6 +106,10 @@
         return signal;
     }
 
+    public void refresh() {
+        mDb.clear();
+
+    }
     /**
      * The DB holds the generated previews for various components. Previews can also have different
      * sizes (landscape vs portrait).
@@ -147,7 +153,7 @@
         values.put(CacheDb.COLUMN_PACKAGE, key.componentName.getPackageName());
         values.put(CacheDb.COLUMN_VERSION, versions[0]);
         values.put(CacheDb.COLUMN_LAST_UPDATED, versions[1]);
-        values.put(CacheDb.COLUMN_PREVIEW_BITMAP, Utilities.flattenBitmap(preview));
+        values.put(CacheDb.COLUMN_PREVIEW_BITMAP, GraphicsUtils.flattenBitmap(preview));
         mDb.insertOrReplace(values);
     }
 
@@ -471,8 +477,9 @@
         RectF boxRect = drawBoxWithShadow(c, size, size);
 
         LauncherIcons li = LauncherIcons.obtain(mContext);
-        Bitmap icon = li.createScaledBitmapWithoutShadow(
-                mutateOnMainThread(info.getFullResIcon(mIconCache)), 0);
+        Bitmap icon = li.createBadgedIconBitmap(
+                mutateOnMainThread(info.getFullResIcon(mIconCache)),
+                Process.myUserHandle(), 0).icon;
         li.recycle();
 
         Rect src = new Rect(0, 0, icon.getWidth(), icon.getHeight());
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 54d29c1..9f846bb 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -19,16 +19,17 @@
 import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
 import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
 import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_TRANSITION_MS;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.SPRING_LOADED;
+import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
 import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_OVERLAY;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.LayoutTransition;
 import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.annotation.SuppressLint;
@@ -44,8 +45,10 @@
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Message;
 import android.os.Parcelable;
 import android.os.UserHandle;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.SparseArray;
@@ -54,6 +57,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.Toast;
 
 import com.android.launcher3.Launcher.LauncherOverlay;
@@ -63,7 +67,7 @@
 import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
 import com.android.launcher3.anim.AnimatorSetBuilder;
 import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.badge.FolderBadgeInfo;
+import com.android.launcher3.dot.FolderDotInfo;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dragndrop.DragController;
@@ -76,16 +80,19 @@
 import com.android.launcher3.folder.PreviewBackground;
 import com.android.launcher3.graphics.DragPreviewProvider;
 import com.android.launcher3.graphics.PreloadIconDrawable;
+import com.android.launcher3.graphics.RotationMode;
 import com.android.launcher3.pageindicators.WorkspacePageIndicator;
 import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
-import com.android.launcher3.touch.ItemLongClickListener;
+import com.android.launcher3.testing.TestProtocol;
 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.IntArray;
+import com.android.launcher3.util.IntSet;
+import com.android.launcher3.util.IntSparseArrayMap;
 import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.LongArrayMap;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.util.WallpaperOffsetInterpolator;
@@ -96,7 +103,7 @@
 
 import java.util.ArrayList;
 import java.util.HashSet;
-import java.util.Set;
+import java.util.function.Predicate;
 
 /**
  * The workspace is a wide area with a wallpaper and a finite number of pages.
@@ -105,8 +112,8 @@
  */
 public class Workspace extends PagedView<WorkspacePageIndicator>
         implements DropTarget, DragSource, View.OnTouchListener,
-        DragController.DragListener, Insettable, LauncherStateManager.StateHandler {
-    private static final String TAG = "Launcher.Workspace";
+        DragController.DragListener, Insettable, LauncherStateManager.StateHandler,
+        WorkspaceLayoutManager {
 
     /** The value that {@link #mTransitionProgress} must be greater than for
      * {@link #transitionStateShouldAllowDrop()} to return true. */
@@ -125,21 +132,16 @@
 
     private static final int DEFAULT_PAGE = 0;
 
-    private static final boolean MAP_NO_RECURSE = false;
-    private static final boolean MAP_RECURSE = true;
-
-    // The screen id used for the empty screen always present to the right.
-    public static final long EXTRA_EMPTY_SCREEN_ID = -201;
-    // The is the first screen. It is always present, even if its empty.
-    public static final long FIRST_SCREEN_ID = 0;
+    public static final boolean MAP_NO_RECURSE = false;
+    public static final boolean MAP_RECURSE = true;
 
     private LayoutTransition mLayoutTransition;
     @Thunk final WallpaperManager mWallpaperManager;
 
     private ShortcutAndWidgetContainer mDragSourceInternal;
 
-    @Thunk final LongArrayMap<CellLayout> mWorkspaceScreens = new LongArrayMap<>();
-    @Thunk final ArrayList<Long> mScreenOrder = new ArrayList<>();
+    @Thunk final IntSparseArrayMap<CellLayout> mWorkspaceScreens = new IntSparseArrayMap<>();
+    @Thunk final IntArray mScreenOrder = new IntArray();
 
     @Thunk Runnable mRemoveEmptyScreenRunnable;
     @Thunk boolean mDeferRemoveExtraEmptyScreen = false;
@@ -173,7 +175,9 @@
     @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];
     private final float[] mTempTouchCoordinates = new float[2];
 
@@ -226,7 +230,7 @@
     @Thunk int mLastReorderY = -1;
 
     private SparseArray<Parcelable> mSavedStates;
-    private final ArrayList<Integer> mRestoredPages = new ArrayList<>();
+    private final IntArray mRestoredPages = new IntArray();
 
     private float mCurrentScale;
     private float mTransitionProgress;
@@ -283,32 +287,39 @@
 
     @Override
     public void setInsets(Rect insets) {
-        mInsets.set(insets);
-
         DeviceProfile grid = mLauncher.getDeviceProfile();
-        mMaxDistanceForFolderCreation = grid.isTablet
-                ? 0.75f * grid.iconSizePx
-                : 0.55f * grid.iconSizePx;
+        DeviceProfile stableGrid = mLauncher.getWallpaperDeviceProfile();
+
+        mMaxDistanceForFolderCreation = stableGrid.isTablet
+                ? 0.75f * stableGrid.iconSizePx
+                : 0.55f * stableGrid.iconSizePx;
         mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens();
 
-        Rect padding = grid.workspacePadding;
-        setPadding(padding.left, padding.top, padding.right, padding.bottom);
+        Rect padding = stableGrid.workspacePadding;
 
-        if (grid.shouldFadeAdjacentWorkspaceScreens()) {
+        RotationMode rotationMode = mLauncher.getRotationMode();
+
+        rotationMode.mapRect(padding, mTempRect);
+        setPadding(mTempRect.left, mTempRect.top, mTempRect.right, mTempRect.bottom);
+        rotationMode.mapRect(stableGrid.getInsets(), mInsets);
+
+        if (mWorkspaceFadeInAdjacentScreens) {
             // In landscape mode the page spacing is set to the default.
-            setPageSpacing(grid.defaultPageSpacingPx);
+            setPageSpacing(grid.edgeMarginPx);
         } else {
             // In portrait, we want the pages spaced such that there is no
             // overhang of the previous / next page into the current page viewport.
             // We assume symmetrical padding in portrait mode.
-            setPageSpacing(Math.max(grid.defaultPageSpacingPx, padding.left + 1));
+            setPageSpacing(Math.max(grid.edgeMarginPx, padding.left + 1));
         }
 
-        int paddingLeftRight = grid.cellLayoutPaddingLeftRightPx;
-        int paddingBottom = grid.cellLayoutBottomPaddingPx;
+
+        int paddingLeftRight = stableGrid.cellLayoutPaddingLeftRightPx;
+        int paddingBottom = stableGrid.cellLayoutBottomPaddingPx;
         for (int i = mWorkspaceScreens.size() - 1; i >= 0; i--) {
-            mWorkspaceScreens.valueAt(i)
-                    .setPadding(paddingLeftRight, 0, paddingLeftRight, paddingBottom);
+            CellLayout page = mWorkspaceScreens.valueAt(i);
+            page.setRotationMode(rotationMode);
+            page.setPadding(paddingLeftRight, 0, paddingLeftRight, paddingBottom);
         }
     }
 
@@ -328,7 +339,7 @@
 
             float scale = 1;
             if (isWidget) {
-                DeviceProfile profile = mLauncher.getDeviceProfile();
+                DeviceProfile profile = mLauncher.getWallpaperDeviceProfile();
                 scale = Utilities.shrinkRect(r, profile.appWidgetScale.x, profile.appWidgetScale.y);
             }
             size[0] = r.width();
@@ -359,6 +370,10 @@
 
     @Override
     public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
+        if (TestProtocol.sDebugTracing) {
+            android.util.Log.d(TestProtocol.NO_DRAG_TAG,
+                    "onDragStart 1");
+        }
         if (ENFORCE_DRAG_EVENT_ORDER) {
             enforceDragParity("onDragStart", 0, 0);
         }
@@ -409,6 +424,10 @@
         }
 
         // Always enter the spring loaded mode
+        if (TestProtocol.sDebugTracing) {
+            android.util.Log.d(TestProtocol.NO_DRAG_TAG,
+                    "onDragStart 2");
+        }
         mLauncher.getStateManager().goToState(SPRING_LOADED);
     }
 
@@ -473,10 +492,6 @@
         super.onViewAdded(child);
     }
 
-    public boolean isTouchActive() {
-        return mTouchState != TOUCH_STATE_REST;
-    }
-
     /**
      * Initializes and binds the first page
      * @param qsb an existing qsb to recycle or null.
@@ -519,6 +534,9 @@
         mScreenOrder.clear();
         mWorkspaceScreens.clear();
 
+        // Remove any deferred refresh callbacks
+        mLauncher.mHandler.removeCallbacksAndMessages(DeferredWidgetRefresh.class);
+
         // Ensure that the first page is always present
         bindAndInitFirstWorkspaceScreen(qsb);
 
@@ -526,7 +544,7 @@
         enableLayoutTransitions();
     }
 
-    public void insertNewWorkspaceScreenBeforeEmptyScreen(long screenId) {
+    public void insertNewWorkspaceScreenBeforeEmptyScreen(int screenId) {
         // Find the index to insert this view into.  If the empty screen exists, then
         // insert it before that.
         int insertIndex = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID);
@@ -536,11 +554,11 @@
         insertNewWorkspaceScreen(screenId, insertIndex);
     }
 
-    public void insertNewWorkspaceScreen(long screenId) {
+    public void insertNewWorkspaceScreen(int screenId) {
         insertNewWorkspaceScreen(screenId, getChildCount());
     }
 
-    public CellLayout insertNewWorkspaceScreen(long screenId, int insertIndex) {
+    public CellLayout insertNewWorkspaceScreen(int screenId, int insertIndex) {
         if (mWorkspaceScreens.containsKey(screenId)) {
             throw new RuntimeException("Screen id " + screenId + " already exists!");
         }
@@ -549,9 +567,10 @@
         // created CellLayout.
         CellLayout newScreen = (CellLayout) LayoutInflater.from(getContext()).inflate(
                         R.layout.workspace_screen, this, false /* attachToRoot */);
-        newScreen.getShortcutsAndWidgets().setId(R.id.workspace_page_container);
-        int paddingLeftRight = mLauncher.getDeviceProfile().cellLayoutPaddingLeftRightPx;
-        int paddingBottom = mLauncher.getDeviceProfile().cellLayoutBottomPaddingPx;
+        DeviceProfile grid = mLauncher.getWallpaperDeviceProfile();
+        int paddingLeftRight = grid.cellLayoutPaddingLeftRightPx;
+        int paddingBottom = grid.cellLayoutBottomPaddingPx;
+        newScreen.setRotationMode(mLauncher.getRotationMode());
         newScreen.setPadding(paddingLeftRight, 0, paddingLeftRight, paddingBottom);
 
         mWorkspaceScreens.put(screenId, newScreen);
@@ -608,7 +627,7 @@
         }
 
         if (hasExtraEmptyScreen() || mScreenOrder.size() == 0) return;
-        long finalScreenId = mScreenOrder.get(mScreenOrder.size() - 1);
+        int finalScreenId = mScreenOrder.get(mScreenOrder.size() - 1);
 
         CellLayout finalScreen = mWorkspaceScreens.get(finalScreenId);
 
@@ -616,14 +635,11 @@
         if (finalScreen.getShortcutsAndWidgets().getChildCount() == 0 &&
                 !finalScreen.isDropPending()) {
             mWorkspaceScreens.remove(finalScreenId);
-            mScreenOrder.remove(finalScreenId);
+            mScreenOrder.removeValue(finalScreenId);
 
             // if this is the last screen, convert it to the empty screen
             mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, finalScreen);
             mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID);
-
-            // Update the model if we have changed any screens
-            LauncherModel.updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
         }
     }
 
@@ -675,9 +691,6 @@
     private void fadeAndRemoveEmptyScreen(int delay, int duration, final Runnable onComplete,
             final boolean stripEmptyScreens) {
         // XXX: Do we need to update LM workspace screens below?
-        PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0f);
-        PropertyValuesHolder bgAlpha = PropertyValuesHolder.ofFloat("backgroundAlpha", 0f);
-
         final CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID);
 
         mRemoveEmptyScreenRunnable = new Runnable() {
@@ -685,7 +698,7 @@
             public void run() {
                 if (hasExtraEmptyScreen()) {
                     mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
-                    mScreenOrder.remove(EXTRA_EMPTY_SCREEN_ID);
+                    mScreenOrder.removeValue(EXTRA_EMPTY_SCREEN_ID);
                     removeView(cl);
                     if (stripEmptyScreens) {
                         stripEmptyScreens();
@@ -696,7 +709,7 @@
             }
         };
 
-        ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(cl, alpha, bgAlpha);
+        ObjectAnimator oa = ObjectAnimator.ofFloat(cl, ALPHA, 0f);
         oa.setDuration(duration);
         oa.setStartDelay(delay);
         oa.addListener(new AnimatorListenerAdapter() {
@@ -717,7 +730,7 @@
         return mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID) && getChildCount() > 1;
     }
 
-    public long commitExtraEmptyScreen() {
+    public int commitExtraEmptyScreen() {
         if (mLauncher.isWorkspaceLoading()) {
             // Invalid and dangerous operation if workspace is loading
             return -1;
@@ -725,25 +738,33 @@
 
         CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID);
         mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
-        mScreenOrder.remove(EXTRA_EMPTY_SCREEN_ID);
+        mScreenOrder.removeValue(EXTRA_EMPTY_SCREEN_ID);
 
-        long newId = LauncherSettings.Settings.call(getContext().getContentResolver(),
+        int newId = LauncherSettings.Settings.call(getContext().getContentResolver(),
                 LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
-                .getLong(LauncherSettings.Settings.EXTRA_VALUE);
+                .getInt(LauncherSettings.Settings.EXTRA_VALUE);
         mWorkspaceScreens.put(newId, cl);
         mScreenOrder.add(newId);
 
-        // Update the model for the new screen
-        LauncherModel.updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
-
         return newId;
     }
 
-    public CellLayout getScreenWithId(long screenId) {
+    @Override
+    public Hotseat getHotseat() {
+        return mLauncher.getHotseat();
+    }
+
+    @Override
+    public void onAddDropTarget(DropTarget target) {
+        mDragController.addDropTarget(target);
+    }
+
+    @Override
+    public CellLayout getScreenWithId(int screenId) {
         return mWorkspaceScreens.get(screenId);
     }
 
-    public long getIdForScreen(CellLayout layout) {
+    public int getIdForScreen(CellLayout layout) {
         int index = mWorkspaceScreens.indexOfValue(layout);
         if (index != -1) {
             return mWorkspaceScreens.keyAt(index);
@@ -751,18 +772,18 @@
         return -1;
     }
 
-    public int getPageIndexForScreenId(long screenId) {
+    public int getPageIndexForScreenId(int screenId) {
         return indexOfChild(mWorkspaceScreens.get(screenId));
     }
 
-    public long getScreenIdForPageIndex(int index) {
+    public int getScreenIdForPageIndex(int index) {
         if (0 <= index && index < mScreenOrder.size()) {
             return mScreenOrder.get(index);
         }
         return -1;
     }
 
-    public ArrayList<Long> getScreenOrder() {
+    public IntArray getScreenOrder() {
         return mScreenOrder;
     }
 
@@ -779,10 +800,10 @@
         }
 
         int currentPage = getNextPage();
-        ArrayList<Long> removeScreens = new ArrayList<>();
+        IntArray removeScreens = new IntArray();
         int total = mWorkspaceScreens.size();
         for (int i = 0; i < total; i++) {
-            long id = mWorkspaceScreens.keyAt(i);
+            int id = mWorkspaceScreens.keyAt(i);
             CellLayout cl = mWorkspaceScreens.valueAt(i);
             // FIRST_SCREEN_ID can never be removed.
             if ((!FeatureFlags.QSB_ON_FIRST_SCREEN || id > FIRST_SCREEN_ID)
@@ -798,10 +819,11 @@
         int minScreens = 1;
 
         int pageShift = 0;
-        for (Long id: removeScreens) {
+        for (int i = 0; i < removeScreens.size(); i++) {
+            int id = removeScreens.get(i);
             CellLayout cl = mWorkspaceScreens.get(id);
             mWorkspaceScreens.remove(id);
-            mScreenOrder.remove(id);
+            mScreenOrder.removeValue(id);
 
             if (getChildCount() > minScreens) {
                 if (indexOfChild(cl) < currentPage) {
@@ -821,118 +843,12 @@
             }
         }
 
-        if (!removeScreens.isEmpty()) {
-            // Update the model if we have changed any screens
-            LauncherModel.updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
-        }
-
         if (pageShift >= 0) {
             setCurrentPage(currentPage - pageShift);
         }
     }
 
     /**
-     * At bind time, we use the rank (screenId) to compute x and y for hotseat items.
-     * See {@link #addInScreen}.
-     */
-    public void addInScreenFromBind(View child, ItemInfo info) {
-        int x = info.cellX;
-        int y = info.cellY;
-        if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
-            int screenId = (int) info.screenId;
-            x = mLauncher.getHotseat().getCellXFromOrder(screenId);
-            y = mLauncher.getHotseat().getCellYFromOrder(screenId);
-        }
-        addInScreen(child, info.container, info.screenId, x, y, info.spanX, info.spanY);
-    }
-
-    /**
-     * Adds the specified child in the specified screen based on the {@param info}
-     * See {@link #addInScreen(View, long, long, int, int, int, int)}.
-     */
-    public void addInScreen(View child, ItemInfo info) {
-        addInScreen(child, info.container, info.screenId, info.cellX, info.cellY,
-                info.spanX, info.spanY);
-    }
-
-    /**
-     * Adds the specified child in the specified screen. The position and dimension of
-     * the child are defined by x, y, spanX and spanY.
-     *
-     * @param child The child to add in one of the workspace's screens.
-     * @param screenId The screen in which to add the child.
-     * @param x The X position of the child in the screen's grid.
-     * @param y The Y position of the child in the screen's grid.
-     * @param spanX The number of cells spanned horizontally by the child.
-     * @param spanY The number of cells spanned vertically by the child.
-     */
-    private void addInScreen(View child, long container, long screenId, int x, int y,
-            int spanX, int spanY) {
-        if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
-            if (getScreenWithId(screenId) == null) {
-                Log.e(TAG, "Skipping child, screenId " + screenId + " not found");
-                // DEBUGGING - Print out the stack trace to see where we are adding from
-                new Throwable().printStackTrace();
-                return;
-            }
-        }
-        if (screenId == EXTRA_EMPTY_SCREEN_ID) {
-            // This should never happen
-            throw new RuntimeException("Screen id should not be EXTRA_EMPTY_SCREEN_ID");
-        }
-
-        final CellLayout layout;
-        if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
-            layout = mLauncher.getHotseat().getLayout();
-
-            // Hide folder title in the hotseat
-            if (child instanceof FolderIcon) {
-                ((FolderIcon) child).setTextVisible(false);
-            }
-        } else {
-            // Show folder title if not in the hotseat
-            if (child instanceof FolderIcon) {
-                ((FolderIcon) child).setTextVisible(true);
-            }
-            layout = getScreenWithId(screenId);
-        }
-
-        ViewGroup.LayoutParams genericLp = child.getLayoutParams();
-        CellLayout.LayoutParams lp;
-        if (genericLp == null || !(genericLp instanceof CellLayout.LayoutParams)) {
-            lp = new CellLayout.LayoutParams(x, y, spanX, spanY);
-        } else {
-            lp = (CellLayout.LayoutParams) genericLp;
-            lp.cellX = x;
-            lp.cellY = y;
-            lp.cellHSpan = spanX;
-            lp.cellVSpan = spanY;
-        }
-
-        if (spanX < 0 && spanY < 0) {
-            lp.isLockedToGrid = false;
-        }
-
-        // Get the canonical child id to uniquely represent this view in this screen
-        ItemInfo info = (ItemInfo) child.getTag();
-        int childId = mLauncher.getViewIdForItem(info);
-
-        boolean markCellsAsOccupied = !(child instanceof Folder);
-        if (!layout.addViewToCellLayout(child, -1, childId, lp, markCellsAsOccupied)) {
-            // TODO: This branch occurs when the workspace is adding views
-            // outside of the defined grid
-            // maybe we should be deleting these items from the LauncherModel?
-            Log.e(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout");
-        }
-
-        child.setHapticFeedbackEnabled(false);
-        child.setOnLongClickListener(ItemLongClickListener.INSTANCE_WORKSPACE);
-        if (child instanceof DropTarget) {
-            mDragController.addDropTarget((DropTarget) child);
-        }
-    }
-
-    /**
      * Called directly from a CellLayout (not by the framework), after we've been added as a
      * listener via setOnInterceptTouchEventListener(). This allows us to tell the CellLayout
      * that it should intercept touch events, which is not something that is normally supported.
@@ -1036,7 +952,7 @@
     }
 
     protected void onScrollInteractionBegin() {
-        super.onScrollInteractionEnd();
+        super.onScrollInteractionBegin();
         mScrollInteractionBegan = true;
     }
 
@@ -1059,7 +975,8 @@
 
     private boolean isScrollingOverlay() {
         return mLauncherOverlay != null &&
-                ((mIsRtl && getUnboundedScrollX() > mMaxScrollX) || (!mIsRtl && getUnboundedScrollX() < 0));
+                ((mIsRtl && getUnboundedScrollX() > mMaxScrollX)
+                        || (!mIsRtl && getUnboundedScrollX() < mMinScrollX));
     }
 
     @Override
@@ -1100,8 +1017,8 @@
     }
 
     @Override
-    protected void overScroll(float amount) {
-        boolean shouldScrollOverlay = mLauncherOverlay != null &&
+    protected void overScroll(int amount) {
+        boolean shouldScrollOverlay = mLauncherOverlay != null && !mScroller.isSpringing() &&
                 ((amount <= 0 && !mIsRtl) || (amount >= 0 && mIsRtl));
 
         boolean shouldZeroOverlay = mLauncherOverlay != null && mLastOverlayScroll != 0 &&
@@ -1113,7 +1030,7 @@
                 mLauncherOverlay.onScrollInteractionBegin();
             }
 
-            mLastOverlayScroll = Math.abs(amount / getMeasuredWidth());
+            mLastOverlayScroll = Math.abs(((float) amount) / getMeasuredWidth());
             mLauncherOverlay.onScrollChange(mLastOverlayScroll, mIsRtl);
         } else {
             dampedOverScroll(amount);
@@ -1291,12 +1208,6 @@
         }
     }
 
-    public void showOutlinesTemporarily() {
-        if (!mIsPageInTransition && !isTouchActive()) {
-            snapToPage(mCurrentPage);
-        }
-    }
-
     private void updatePageAlphaValues() {
         // We need to check the isDragging case because updatePageAlphaValues is called between
         // goToState(SPRING_LOADED) and onStartStateTransition.
@@ -1483,6 +1394,10 @@
         builder.play(stepAnimator);
     }
 
+    public WorkspaceStateTransitionAnimation getStateTransitionAnimation() {
+        return mStateTransitionAnimation;
+    }
+
     public void updateAccessibilityFlags() {
         // TODO: Update the accessibility flags appropriately when dragging.
         int accessibilityFlag = mLauncher.getStateManager().getState().workspaceAccessibilityFlag;
@@ -1495,6 +1410,18 @@
         }
     }
 
+    @Override
+    public AccessibilityNodeInfo createAccessibilityNodeInfo() {
+        if (getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS) {
+            // TAPL tests verify that workspace is not present in Overview and AllApps states.
+            // 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 super.createAccessibilityNodeInfo();
+    }
+
     private void updateAccessibilityFlags(int accessibilityFlag, CellLayout page) {
         page.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
         page.getShortcutsAndWidgets().setImportantForAccessibility(accessibilityFlag);
@@ -1514,7 +1441,7 @@
                 @Override
                 protected void enableAccessibleDrag(boolean enable) {
                     super.enableAccessibleDrag(enable);
-                    setEnableForLayout(mLauncher.getHotseat().getLayout(),enable);
+                    setEnableForLayout(mLauncher.getHotseat(),enable);
                 }
             });
         }
@@ -1622,11 +1549,7 @@
             mDragViewVisualCenter = d.getVisualCenter(mDragViewVisualCenter);
 
             // We want the point to be mapped to the dragTarget.
-            if (mLauncher.isHotseatLayout(dropTargetLayout)) {
-                mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter);
-            } else {
-                mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter);
-            }
+            mapPointFromDropLayout(dropTargetLayout, mDragViewVisualCenter);
 
             int spanX;
             int spanY;
@@ -1674,7 +1597,7 @@
             }
         }
 
-        long screenId = getIdForScreen(dropTargetLayout);
+        int screenId = getIdForScreen(dropTargetLayout);
         if (screenId == EXTRA_EMPTY_SCREEN_ID) {
             commitExtraEmptyScreen();
         }
@@ -1706,9 +1629,9 @@
             return false;
         }
 
-        boolean aboveShortcut = (dropOverView.getTag() instanceof ShortcutInfo);
+        boolean aboveShortcut = (dropOverView.getTag() instanceof WorkspaceItemInfo);
         boolean willBecomeShortcut =
-                (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
+                (info.itemType == ITEM_TYPE_APPLICATION ||
                         info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT ||
                         info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT);
 
@@ -1739,7 +1662,7 @@
         return false;
     }
 
-    boolean createUserFolderIfNecessary(View newView, long container, CellLayout target,
+    boolean createUserFolderIfNecessary(View newView, int container, CellLayout target,
             int[] targetCell, float distance, boolean external, DragView dragView) {
         if (distance > mMaxDistanceForFolderCreation) return false;
         View v = target.getChildAt(targetCell[0], targetCell[1]);
@@ -1753,14 +1676,14 @@
 
         if (v == null || hasntMoved || !mCreateUserFolderOnDrop) return false;
         mCreateUserFolderOnDrop = false;
-        final long screenId = getIdForScreen(target);
+        final int screenId = getIdForScreen(target);
 
-        boolean aboveShortcut = (v.getTag() instanceof ShortcutInfo);
-        boolean willBecomeShortcut = (newView.getTag() instanceof ShortcutInfo);
+        boolean aboveShortcut = (v.getTag() instanceof WorkspaceItemInfo);
+        boolean willBecomeShortcut = (newView.getTag() instanceof WorkspaceItemInfo);
 
         if (aboveShortcut && willBecomeShortcut) {
-            ShortcutInfo sourceInfo = (ShortcutInfo) newView.getTag();
-            ShortcutInfo destInfo = (ShortcutInfo) v.getTag();
+            WorkspaceItemInfo sourceInfo = (WorkspaceItemInfo) newView.getTag();
+            WorkspaceItemInfo destInfo = (WorkspaceItemInfo) v.getTag();
             // if the drag started here, we need to remove it from the workspace
             if (!external) {
                 getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
@@ -1828,11 +1751,7 @@
 
         // We want the point to be mapped to the dragTarget.
         if (dropTargetLayout != null) {
-            if (mLauncher.isHotseatLayout(dropTargetLayout)) {
-                mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter);
-            } else {
-                mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter);
-            }
+            mapPointFromDropLayout(dropTargetLayout, mDragViewVisualCenter);
         }
 
         boolean droppedOnOriginalCell = false;
@@ -1852,10 +1771,10 @@
                 // Move internally
                 boolean hasMovedLayouts = (getParentCellLayoutForView(cell) != dropTargetLayout);
                 boolean hasMovedIntoHotseat = mLauncher.isHotseatLayout(dropTargetLayout);
-                long container = hasMovedIntoHotseat ?
+                int container = hasMovedIntoHotseat ?
                         LauncherSettings.Favorites.CONTAINER_HOTSEAT :
                         LauncherSettings.Favorites.CONTAINER_DESKTOP;
-                long screenId = (mTargetCell[0] < 0) ?
+                int screenId = (mTargetCell[0] < 0) ?
                         mDragInfo.screenId : getIdForScreen(dropTargetLayout);
                 int spanX = mDragInfo != null ? mDragInfo.spanX : 1;
                 int spanY = mDragInfo != null ? mDragInfo.spanY : 1;
@@ -2021,22 +1940,8 @@
     }
 
     public void onNoCellFound(View dropTargetLayout) {
-        if (mLauncher.isHotseatLayout(dropTargetLayout)) {
-            Hotseat hotseat = mLauncher.getHotseat();
-            boolean droppedOnAllAppsIcon = !FeatureFlags.NO_ALL_APPS_ICON
-                    && mTargetCell != null && !mLauncher.getDeviceProfile().inv.isAllAppsButtonRank(
-                    hotseat.getOrderInHotseat(mTargetCell[0], mTargetCell[1]));
-            if (!droppedOnAllAppsIcon) {
-                // Only show message when hotseat is full and drop target was not AllApps button
-                showOutOfSpaceMessage(true);
-            }
-        } else {
-            showOutOfSpaceMessage(false);
-        }
-    }
-
-    private void showOutOfSpaceMessage(boolean isHotseatLayout) {
-        int strId = (isHotseatLayout ? R.string.hotseat_out_of_space : R.string.out_of_space);
+        int strId = mLauncher.isHotseatLayout(dropTargetLayout)
+                ? R.string.hotseat_out_of_space : R.string.out_of_space;
         Toast.makeText(mLauncher, mLauncher.getString(strId), Toast.LENGTH_SHORT).show();
     }
 
@@ -2048,18 +1953,9 @@
         if (child == null) {
             return;
         }
+
         ShortcutAndWidgetContainer boundingLayout = child.getShortcutsAndWidgets();
-
-        // Use the absolute left instead of the child left, as we want the visible area
-        // irrespective of the visible child. Since the view can only scroll horizontally, the
-        // top position is not affected.
-        mTempXY[0] = getPaddingLeft() + boundingLayout.getLeft();
-        mTempXY[1] = child.getTop() + boundingLayout.getTop();
-
-        float scale = mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempXY);
-        outArea.set(mTempXY[0], mTempXY[1],
-                (int) (mTempXY[0] + scale * boundingLayout.getMeasuredWidth()),
-                (int) (mTempXY[1] + scale * boundingLayout.getMeasuredHeight()));
+        mLauncher.getDragLayer().getDescendantRectRelativeToSelf(boundingLayout, outArea);
     }
 
     @Override
@@ -2206,30 +2102,34 @@
     * 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.
     */
-   void mapPointFromSelfToChild(View v, float[] xy) {
+   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) {
-       mTempXY[0] = x;
-       mTempXY[1] = y;
-       mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempXY, true);
+       mTempFXY[0] = x;
+       mTempFXY[1] = y;
+       mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempFXY, true);
        View hotseat = mLauncher.getHotseat();
-       return mTempXY[0] >= hotseat.getLeft() &&
-               mTempXY[0] <= hotseat.getRight() &&
-               mTempXY[1] >= hotseat.getTop() &&
-               mTempXY[1] <= hotseat.getBottom();
+       return mTempFXY[0] >= hotseat.getLeft() &&
+               mTempFXY[0] <= hotseat.getRight() &&
+               mTempFXY[1] >= hotseat.getTop() &&
+               mTempFXY[1] <= hotseat.getBottom();
    }
 
-   void mapPointFromSelfToHotseatLayout(Hotseat hotseat, float[] xy) {
-       mTempXY[0] = (int) xy[0];
-       mTempXY[1] = (int) xy[1];
-       mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempXY, true);
-       mLauncher.getDragLayer().mapCoordInSelfToDescendant(hotseat.getLayout(), mTempXY);
-
-       xy[0] = mTempXY[0];
-       xy[1] = mTempXY[1];
+    /**
+     * 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 boolean isDragWidget(DragObject d) {
@@ -2265,11 +2165,7 @@
         // Handle the drag over
         if (mDragTargetLayout != null) {
             // We want the point to be mapped to the dragTarget.
-            if (mLauncher.isHotseatLayout(mDragTargetLayout)) {
-                mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter);
-            } else {
-                mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter);
-            }
+            mapPointFromDropLayout(mDragTargetLayout, mDragViewVisualCenter);
 
             int minSpanX = item.spanX;
             int minSpanY = item.spanY;
@@ -2340,7 +2236,7 @@
         // Test to see if we are over the hotseat first
         if (mLauncher.getHotseat() != null && !isDragWidget(d)) {
             if (isPointInSelfOverHotseat(d.x, d.y)) {
-                layout = mLauncher.getHotseat().getLayout();
+                layout = mLauncher.getHotseat();
             }
         }
 
@@ -2456,7 +2352,7 @@
             this.cellY = cellY;
 
             BubbleTextView cell = (BubbleTextView) layout.getChildAt(cellX, cellY);
-            bg.setup(mLauncher, null, cell.getMeasuredWidth(), cell.getPaddingTop());
+            bg.setup(mLauncher, mLauncher, null, cell.getMeasuredWidth(), cell.getPaddingTop());
 
             // The full preview background should appear behind the icon
             bg.isClipping = false;
@@ -2528,8 +2424,8 @@
      */
     private void onDropExternal(final int[] touchXY, final CellLayout cellLayout, DragObject d) {
         if (d.dragInfo instanceof PendingAddShortcutInfo) {
-            ShortcutInfo si = ((PendingAddShortcutInfo) d.dragInfo)
-                    .activityInfo.createShortcutInfo();
+            WorkspaceItemInfo si = ((PendingAddShortcutInfo) d.dragInfo)
+                    .activityInfo.createWorkspaceItemInfo();
             if (si != null) {
                 d.dragInfo = si;
             }
@@ -2543,10 +2439,10 @@
             spanY = mDragInfo.spanY;
         }
 
-        final long container = mLauncher.isHotseatLayout(cellLayout) ?
+        final int container = mLauncher.isHotseatLayout(cellLayout) ?
                 LauncherSettings.Favorites.CONTAINER_HOTSEAT :
                     LauncherSettings.Favorites.CONTAINER_DESKTOP;
-        final long screenId = getIdForScreen(cellLayout);
+        final int screenId = getIdForScreen(cellLayout);
         if (!mLauncher.isHotseatLayout(cellLayout)
                 && screenId != getScreenIdForPageIndex(mCurrentPage)
                 && !mLauncher.isInState(SPRING_LOADED)) {
@@ -2629,21 +2525,15 @@
             View view;
 
             switch (info.itemType) {
-            case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
+            case ITEM_TYPE_APPLICATION:
             case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
             case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
-                if (info.container == NO_ID) {
+                if (info.container == NO_ID && info instanceof AppInfo) {
                     // Came from all apps -- make a copy
-                    if (info instanceof AppInfo) {
-                        info = ((AppInfo) info).makeShortcut();
-                        d.dragInfo = info;
-                    } else if (info instanceof ShortcutInfo) {
-                        info = new ShortcutInfo((ShortcutInfo) info);
-                        d.dragInfo = info;
-                    }
-
+                    info = ((AppInfo) info).makeWorkspaceItem();
+                    d.dragInfo = info;
                 }
-                view = mLauncher.createShortcut(cellLayout, (ShortcutInfo) info);
+                view = mLauncher.createShortcut(cellLayout, (WorkspaceItemInfo) info);
                 break;
             case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
                 view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, cellLayout,
@@ -2727,13 +2617,14 @@
             DeviceProfile profile = mLauncher.getDeviceProfile();
             Utilities.shrinkRect(r, profile.appWidgetScale.x, profile.appWidgetScale.y);
         }
-        loc[0] = r.left;
-        loc[1] = r.top;
 
+        mTempFXY[0] = r.left;
+        mTempFXY[1] = r.top;
         setFinalTransitionTransform();
         float cellLayoutScale =
-                mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(layout, loc, true);
+                mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(layout, mTempFXY, true);
         resetTransitionTransform();
+        Utilities.roundArray(mTempFXY, loc);
 
         if (scale) {
             float dragViewScaleX = (1.0f * r.width()) / dragView.getMeasuredWidth();
@@ -2866,7 +2757,6 @@
      */
     public void onDropCompleted(final View target, final DragObject d,
             final boolean success) {
-
         if (success) {
             if (target != this && mDragInfo != null) {
                 removeWorkspaceItem(mDragInfo.cell);
@@ -2985,8 +2875,7 @@
      * Returns a specific CellLayout
      */
     CellLayout getParentCellLayoutForView(View v) {
-        ArrayList<CellLayout> layouts = getWorkspaceAndHotseatCellLayouts();
-        for (CellLayout layout : layouts) {
+        for (CellLayout layout : getWorkspaceAndHotseatCellLayouts()) {
             if (layout.getShortcutsAndWidgets().indexOfChild(v) > -1) {
                 return layout;
             }
@@ -2995,66 +2884,71 @@
     }
 
     /**
-     * Returns a list of all the CellLayouts in the workspace.
+     * Returns a list of all the CellLayouts on the Homescreen.
      */
-    ArrayList<CellLayout> getWorkspaceAndHotseatCellLayouts() {
-        ArrayList<CellLayout> layouts = new ArrayList<>();
+    private CellLayout[] getWorkspaceAndHotseatCellLayouts() {
         int screenCount = getChildCount();
-        for (int screen = 0; screen < screenCount; screen++) {
-            layouts.add(((CellLayout) getChildAt(screen)));
-        }
+        final CellLayout[] layouts;
         if (mLauncher.getHotseat() != null) {
-            layouts.add(mLauncher.getHotseat().getLayout());
+            layouts = new CellLayout[screenCount + 1];
+            layouts[screenCount] = mLauncher.getHotseat();
+        } else {
+            layouts = new CellLayout[screenCount];
+        }
+        for (int screen = 0; screen < screenCount; screen++) {
+            layouts[screen] = (CellLayout) getChildAt(screen);
         }
         return layouts;
     }
 
     /**
-     * We should only use this to search for specific children.  Do not use this method to modify
-     * ShortcutsAndWidgetsContainer directly. Includes ShortcutAndWidgetContainers from
-     * the hotseat and workspace pages
+     * Similar to {@link #getFirstMatch} but optimized to finding a suitable view for the app close
+     * animation.
+     *
+     * @param packageName The package name of the app to match.
+     * @param user The user of the app to match.
      */
-    ArrayList<ShortcutAndWidgetContainer> getAllShortcutAndWidgetContainers() {
-        ArrayList<ShortcutAndWidgetContainer> childrenLayouts = new ArrayList<>();
-        int screenCount = getChildCount();
-        for (int screen = 0; screen < screenCount; screen++) {
-            childrenLayouts.add(((CellLayout) getChildAt(screen)).getShortcutsAndWidgets());
+    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);
+        final Workspace.ItemOperator packageAndUserAndApp = (ItemInfo info, View view) ->
+                packageAndUser.evaluate(info, view) && info.itemType == ITEM_TYPE_APPLICATION;
+        final Workspace.ItemOperator packageAndUserAndAppInFolder = (info, view) -> {
+            if (info instanceof FolderInfo) {
+                FolderInfo folderInfo = (FolderInfo) info;
+                for (WorkspaceItemInfo shortcutInfo : folderInfo.contents) {
+                    if (packageAndUserAndApp.evaluate(shortcutInfo, view)) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        };
+
+        // Order: App icons, app in folder. Items in hotseat get returned first.
+        if (ADAPTIVE_ICON_WINDOW_ANIM.get()) {
+            return getFirstMatch(new CellLayout[] { getHotseat(), currentPage },
+                    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);
         }
-        if (mLauncher.getHotseat() != null) {
-            childrenLayouts.add(mLauncher.getHotseat().getLayout().getShortcutsAndWidgets());
-        }
-        return childrenLayouts;
     }
 
-    public View getHomescreenIconByItemId(final long id) {
-        return getFirstMatch(new ItemOperator() {
-
-            @Override
-            public boolean evaluate(ItemInfo info, View v) {
-                return info != null && info.id == id;
-            }
-        });
-    }
-
-    public View getViewForTag(final Object tag) {
-        return getFirstMatch(new ItemOperator() {
-
-            @Override
-            public boolean evaluate(ItemInfo info, View v) {
-                return info == tag;
-            }
-        });
+    public View getHomescreenIconByItemId(final int id) {
+        return getFirstMatch((info, v) -> info != null && info.id == id);
     }
 
     public LauncherAppWidgetHostView getWidgetForAppWidgetId(final int appWidgetId) {
-        return (LauncherAppWidgetHostView) getFirstMatch(new ItemOperator() {
-
-            @Override
-            public boolean evaluate(ItemInfo info, View v) {
-                return (info instanceof LauncherAppWidgetInfo) &&
-                        ((LauncherAppWidgetInfo) info).appWidgetId == appWidgetId;
-            }
-        });
+        return (LauncherAppWidgetHostView) getFirstMatch((info, v) ->
+                (info instanceof LauncherAppWidgetInfo) &&
+                        ((LauncherAppWidgetInfo) info).appWidgetId == appWidgetId);
     }
 
     public View getFirstMatch(final ItemOperator operator) {
@@ -3072,6 +2966,40 @@
         return value[0];
     }
 
+    /**
+     * @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(MAP_NO_RECURSE, 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;
+                        }
+                    }
+                }
+                return false;
+            });
+            if (matches[0] != null) {
+                break;
+            }
+        }
+        for (View match : matches) {
+            if (match != null) {
+                return match;
+            }
+        }
+        return null;
+    }
+
     void clearDropTargets() {
         mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
             @Override
@@ -3091,11 +3019,10 @@
      * shortcuts are not removed.
      */
     public void removeItemsByMatcher(final ItemInfoMatcher matcher) {
-        ArrayList<CellLayout> cellLayouts = getWorkspaceAndHotseatCellLayouts();
-        for (final CellLayout layoutParent: cellLayouts) {
+        for (final CellLayout layoutParent: getWorkspaceAndHotseatCellLayouts()) {
             final ViewGroup layout = layoutParent.getShortcutsAndWidgets();
 
-            LongArrayMap<View> idToViewMap = new LongArrayMap<>();
+            IntSparseArrayMap<View> idToViewMap = new IntSparseArrayMap<>();
             ArrayList<ItemInfo> items = new ArrayList<>();
             for (int j = 0; j < layout.getChildCount(); j++) {
                 final View view = layout.getChildAt(j);
@@ -3122,7 +3049,7 @@
                     if (parent != null) {
                         FolderInfo folderInfo = (FolderInfo) parent.getTag();
                         folderInfo.prepareAutoUpdate();
-                        folderInfo.remove((ShortcutInfo) itemToRemove, false);
+                        folderInfo.remove((WorkspaceItemInfo) itemToRemove, false);
                     }
                 }
             }
@@ -3149,44 +3076,53 @@
      * @param recurse true: iterate over folder children. false: op get the folders themselves.
      * @param op the operator to map over the shortcuts
      */
-    void mapOverItems(boolean recurse, ItemOperator op) {
-        ArrayList<ShortcutAndWidgetContainer> containers = getAllShortcutAndWidgetContainers();
-        final int containerCount = containers.size();
-        for (int containerIdx = 0; containerIdx < containerCount; containerIdx++) {
-            ShortcutAndWidgetContainer container = containers.get(containerIdx);
-            // map over all the shortcuts on the workspace
-            final int itemCount = container.getChildCount();
-            for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) {
-                View item = container.getChildAt(itemIdx);
-                ItemInfo info = (ItemInfo) item.getTag();
-                if (recurse && info instanceof FolderInfo && item instanceof FolderIcon) {
-                    FolderIcon folder = (FolderIcon) item;
-                    ArrayList<View> folderChildren = folder.getFolder().getItemsInReadingOrder();
-                    // map over all the children in the folder
-                    final int childCount = folderChildren.size();
-                    for (int childIdx = 0; childIdx < childCount; childIdx++) {
-                        View child = folderChildren.get(childIdx);
-                        info = (ItemInfo) child.getTag();
-                        if (op.evaluate(info, child)) {
-                            return;
-                        }
-                    }
-                } else {
-                    if (op.evaluate(info, item)) {
-                        return;
-                    }
-                }
+    public void mapOverItems(boolean recurse, ItemOperator op) {
+        for (CellLayout layout : getWorkspaceAndHotseatCellLayouts()) {
+            if (mapOverCellLayout(recurse, layout, op)) {
+                return;
             }
         }
     }
 
-    void updateShortcuts(ArrayList<ShortcutInfo> shortcuts) {
+    private boolean mapOverCellLayout(boolean recurse, CellLayout layout, ItemOperator op) {
+        // TODO(b/128460496) Potential race condition where layout is not yet loaded
+        if (layout == null) {
+            return false;
+        }
+        ShortcutAndWidgetContainer container = layout.getShortcutsAndWidgets();
+        // map over all the shortcuts on the workspace
+        final int itemCount = container.getChildCount();
+        for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) {
+            View item = container.getChildAt(itemIdx);
+            ItemInfo info = (ItemInfo) item.getTag();
+            if (recurse && info instanceof FolderInfo && item instanceof FolderIcon) {
+                FolderIcon folder = (FolderIcon) item;
+                ArrayList<View> folderChildren = folder.getFolder().getItemsInReadingOrder();
+                // map over all the children in the folder
+                final int childCount = folderChildren.size();
+                for (int childIdx = 0; childIdx < childCount; childIdx++) {
+                    View child = folderChildren.get(childIdx);
+                    info = (ItemInfo) child.getTag();
+                    if (op.evaluate(info, child)) {
+                        return true;
+                    }
+                }
+            } else {
+                if (op.evaluate(info, item)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    void updateShortcuts(ArrayList<WorkspaceItemInfo> shortcuts) {
         int total  = shortcuts.size();
-        final HashSet<ShortcutInfo> updates = new HashSet<>(total);
-        final HashSet<Long> folderIds = new HashSet<>();
+        final HashSet<WorkspaceItemInfo> updates = new HashSet<>(total);
+        final IntSet folderIds = new IntSet();
 
         for (int i = 0; i < total; i++) {
-            ShortcutInfo s = shortcuts.get(i);
+            WorkspaceItemInfo s = shortcuts.get(i);
             updates.add(s);
             folderIds.add(s.container);
         }
@@ -3194,14 +3130,14 @@
         mapOverItems(MAP_RECURSE, new ItemOperator() {
             @Override
             public boolean evaluate(ItemInfo info, View v) {
-                if (info instanceof ShortcutInfo && v instanceof BubbleTextView &&
+                if (info instanceof WorkspaceItemInfo && v instanceof BubbleTextView &&
                         updates.contains(info)) {
-                    ShortcutInfo si = (ShortcutInfo) info;
+                    WorkspaceItemInfo si = (WorkspaceItemInfo) info;
                     BubbleTextView shortcut = (BubbleTextView) v;
                     Drawable oldIcon = shortcut.getIcon();
                     boolean oldPromiseState = (oldIcon instanceof PreloadIconDrawable)
                             && ((PreloadIconDrawable) oldIcon).hasNotCompleted();
-                    shortcut.applyFromShortcutInfo(si, si.isPromise() != oldPromiseState);
+                    shortcut.applyFromWorkspaceItem(si, si.isPromise() != oldPromiseState);
                 }
                 // process all the shortcuts
                 return false;
@@ -3221,16 +3157,16 @@
         });
     }
 
-    public void updateIconBadges(final Set<PackageUserKey> updatedBadges) {
+    public void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
         final PackageUserKey packageUserKey = new PackageUserKey(null, null);
-        final HashSet<Long> folderIds = new HashSet<>();
+        final IntSet folderIds = new IntSet();
         mapOverItems(MAP_RECURSE, new ItemOperator() {
             @Override
             public boolean evaluate(ItemInfo info, View v) {
-                if (info instanceof ShortcutInfo && v instanceof BubbleTextView
-                        && packageUserKey.updateFromItemInfo(info)) {
-                    if (updatedBadges.contains(packageUserKey)) {
-                        ((BubbleTextView) v).applyBadgeState(info, true /* animate */);
+                if (info instanceof WorkspaceItemInfo && v instanceof BubbleTextView) {
+                    if (!packageUserKey.updateFromItemInfo(info)
+                            || updatedDots.test(packageUserKey)) {
+                        ((BubbleTextView) v).applyDotState(info, true /* animate */);
                         folderIds.add(info.container);
                     }
                 }
@@ -3245,11 +3181,11 @@
             public boolean evaluate(ItemInfo info, View v) {
                 if (info instanceof FolderInfo && folderIds.contains(info.id)
                         && v instanceof FolderIcon) {
-                    FolderBadgeInfo folderBadgeInfo = new FolderBadgeInfo();
-                    for (ShortcutInfo si : ((FolderInfo) info).contents) {
-                        folderBadgeInfo.addBadgeInfo(mLauncher.getBadgeInfoForItem(si));
+                    FolderDotInfo folderDotInfo = new FolderDotInfo();
+                    for (WorkspaceItemInfo si : ((FolderInfo) info).contents) {
+                        folderDotInfo.addDotInfo(mLauncher.getDotInfoForItem(si));
                     }
-                    ((FolderIcon) v).setBadgeInfo(folderBadgeInfo);
+                    ((FolderIcon) v).setDotInfo(folderDotInfo);
                 }
                 // process all the shortcuts
                 return false;
@@ -3269,7 +3205,7 @@
         mapOverItems(MAP_RECURSE, new ItemOperator() {
             @Override
             public boolean evaluate(ItemInfo info, View v) {
-                if (info instanceof ShortcutInfo && v instanceof BubbleTextView
+                if (info instanceof WorkspaceItemInfo && v instanceof BubbleTextView
                         && updates.contains(info)) {
                     ((BubbleTextView) v).applyPromiseState(false /* promiseStateChanged */);
                 } else if (v instanceof PendingAppWidgetHostView
@@ -3401,13 +3337,15 @@
             LauncherAppWidgetHost host) {
             mInfos = infos;
             mHost = host;
-            mHandler = new Handler();
+            mHandler = mLauncher.mHandler;
             mRefreshPending = true;
 
             mHost.addProviderChangeListener(this);
             // Force refresh after 10 seconds, if we don't get the provider changed event.
             // This could happen when the provider is no longer available in the app.
-            mHandler.postDelayed(this, 10000);
+            Message msg = Message.obtain(mHandler, this);
+            msg.obj = DeferredWidgetRefresh.class;
+            mHandler.sendMessageDelayed(msg, 10000);
         }
 
         @Override
diff --git a/src/com/android/launcher3/WorkspaceItemInfo.java b/src/com/android/launcher3/WorkspaceItemInfo.java
new file mode 100644
index 0000000..5a2373b
--- /dev/null
+++ b/src/com/android/launcher3/WorkspaceItemInfo.java
@@ -0,0 +1,199 @@
+/*
+ * 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 android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ShortcutInfo;
+import android.text.TextUtils;
+
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.util.ContentWriter;
+
+/**
+ * Represents a launchable icon on the workspaces and in folders.
+ */
+public class WorkspaceItemInfo extends ItemInfoWithIcon {
+
+    public static final int DEFAULT = 0;
+
+    /**
+     * The shortcut was restored from a backup and it not ready to be used. This is automatically
+     * set during backup/restore
+     */
+    public static final int FLAG_RESTORED_ICON = 1;
+
+    /**
+     * The icon was added as an auto-install app, and is not ready to be used. This flag can't
+     * be present along with {@link #FLAG_RESTORED_ICON}, and is set during default layout
+     * parsing.
+     */
+    public static final int FLAG_AUTOINSTALL_ICON = 2; //0B10;
+
+    /**
+     * 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 = 4; // 0B100;
+
+    /**
+     * Indicates that the widget restore has started.
+     */
+    public static final int FLAG_RESTORE_STARTED = 8; //0B1000;
+
+    /**
+     * Web UI supported.
+     */
+    public static final int FLAG_SUPPORTS_WEB_UI = 16; //0B10000;
+
+    /**
+     * The intent used to start the application.
+     */
+    public Intent intent;
+
+    /**
+     * If isShortcut=true and customIcon=false, this contains a reference to the
+     * shortcut icon as an application's resource.
+     */
+    public Intent.ShortcutIconResource iconResource;
+
+    /**
+     * A message to display when the user tries to start a disabled shortcut.
+     * This is currently only used for deep shortcuts.
+     */
+    public CharSequence disabledMessage;
+
+    public int status;
+
+    /**
+     * The installation progress [0-100] of the package that this shortcut represents.
+     */
+    private int mInstallProgress;
+
+    public WorkspaceItemInfo() {
+        itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
+    }
+
+    public WorkspaceItemInfo(WorkspaceItemInfo info) {
+        super(info);
+        title = info.title;
+        intent = new Intent(info.intent);
+        iconResource = info.iconResource;
+        status = info.status;
+        mInstallProgress = info.mInstallProgress;
+    }
+
+    /** TODO: Remove this.  It's only called by ApplicationInfo.makeWorkspaceItem. */
+    public WorkspaceItemInfo(AppInfo info) {
+        super(info);
+        title = Utilities.trim(info.title);
+        intent = new Intent(info.intent);
+    }
+
+    /**
+     * Creates a {@link WorkspaceItemInfo} from a {@link ShortcutInfo}.
+     */
+    public WorkspaceItemInfo(ShortcutInfo shortcutInfo, Context context) {
+        user = shortcutInfo.getUserHandle();
+        itemType = Favorites.ITEM_TYPE_DEEP_SHORTCUT;
+        updateFromDeepShortcutInfo(shortcutInfo, context);
+    }
+
+    @Override
+    public void onAddToDatabase(ContentWriter writer) {
+        super.onAddToDatabase(writer);
+        writer.put(Favorites.TITLE, title)
+                .put(Favorites.INTENT, getIntent())
+                .put(Favorites.RESTORED, status);
+
+        if (!usingLowResIcon()) {
+            writer.putIcon(iconBitmap, user);
+        }
+        if (iconResource != null) {
+            writer.put(Favorites.ICON_PACKAGE, iconResource.packageName)
+                    .put(Favorites.ICON_RESOURCE, iconResource.resourceName);
+        }
+    }
+
+    @Override
+    public Intent getIntent() {
+        return intent;
+    }
+
+    public boolean hasStatusFlag(int flag) {
+        return (status & flag) != 0;
+    }
+
+
+    public final boolean isPromise() {
+        return hasStatusFlag(FLAG_RESTORED_ICON | FLAG_AUTOINSTALL_ICON);
+    }
+
+    public boolean hasPromiseIconUi() {
+        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);
+        title = shortcutInfo.getShortLabel();
+
+        CharSequence label = shortcutInfo.getLongLabel();
+        if (TextUtils.isEmpty(label)) {
+            label = shortcutInfo.getShortLabel();
+        }
+        contentDescription = context.getPackageManager().getUserBadgedLabel(label, user);
+        if (shortcutInfo.isEnabled()) {
+            runtimeStatusFlags &= ~FLAG_DISABLED_BY_PUBLISHER;
+        } else {
+            runtimeStatusFlags |= FLAG_DISABLED_BY_PUBLISHER;
+        }
+        disabledMessage = shortcutInfo.getDisabledMessage();
+    }
+
+    /** Returns the WorkspaceItemInfo id associated with the deep shortcut. */
+    public String getDeepShortcutId() {
+        return itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT ?
+                getIntent().getStringExtra(ShortcutKey.EXTRA_SHORTCUT_ID) : null;
+    }
+
+    @Override
+    public ComponentName getTargetComponent() {
+        ComponentName cn = super.getTargetComponent();
+        if (cn == null && (itemType == Favorites.ITEM_TYPE_SHORTCUT
+                || hasStatusFlag(FLAG_SUPPORTS_WEB_UI))) {
+            // 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
+            // check everywhere.
+            String pkg = intent.getPackage();
+            return pkg == null ? null : new ComponentName(pkg, IconCache.EMPTY_CLASS_NAME);
+        }
+        return cn;
+    }
+}
diff --git a/src/com/android/launcher3/WorkspaceLayoutManager.java b/src/com/android/launcher3/WorkspaceLayoutManager.java
new file mode 100644
index 0000000..ea2d4d0
--- /dev/null
+++ b/src/com/android/launcher3/WorkspaceLayoutManager.java
@@ -0,0 +1,141 @@
+/*
+ * 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.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.launcher3.folder.Folder;
+import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.touch.ItemLongClickListener;
+
+public interface WorkspaceLayoutManager {
+
+    String TAG = "Launcher.Workspace";
+
+    // The screen id used for the empty screen always present to the right.
+    int EXTRA_EMPTY_SCREEN_ID = -201;
+    // The is the first screen. It is always present, even if its empty.
+    int FIRST_SCREEN_ID = 0;
+
+    /**
+     * At bind time, we use the rank (screenId) to compute x and y for hotseat items.
+     * See {@link #addInScreen}.
+     */
+    default void addInScreenFromBind(View child, ItemInfo info) {
+        int x = info.cellX;
+        int y = info.cellY;
+        if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
+            int screenId = info.screenId;
+            x = getHotseat().getCellXFromOrder(screenId);
+            y = getHotseat().getCellYFromOrder(screenId);
+        }
+        addInScreen(child, info.container, info.screenId, x, y, info.spanX, info.spanY);
+    }
+
+    /**
+     * Adds the specified child in the specified screen based on the {@param info}
+     * See {@link #addInScreen(View, int, int, int, int, int, int)}.
+     */
+    default void addInScreen(View child, ItemInfo info) {
+        addInScreen(child, info.container, info.screenId, info.cellX, info.cellY,
+                info.spanX, info.spanY);
+    }
+
+    /**
+     * Adds the specified child in the specified screen. The position and dimension of
+     * the child are defined by x, y, spanX and spanY.
+     *
+     * @param child The child to add in one of the workspace's screens.
+     * @param screenId The screen in which to add the child.
+     * @param x The X position of the child in the screen's grid.
+     * @param y The Y position of the child in the screen's grid.
+     * @param spanX The number of cells spanned horizontally by the child.
+     * @param spanY The number of cells spanned vertically by the child.
+     */
+    default void addInScreen(View child, int container, int screenId, int x, int y,
+            int spanX, int spanY) {
+        if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+            if (getScreenWithId(screenId) == null) {
+                Log.e(TAG, "Skipping child, screenId " + screenId + " not found");
+                // DEBUGGING - Print out the stack trace to see where we are adding from
+                new Throwable().printStackTrace();
+                return;
+            }
+        }
+        if (screenId == EXTRA_EMPTY_SCREEN_ID) {
+            // This should never happen
+            throw new RuntimeException("Screen id should not be EXTRA_EMPTY_SCREEN_ID");
+        }
+
+        final CellLayout layout;
+        if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
+            layout = getHotseat();
+
+            // Hide folder title in the hotseat
+            if (child instanceof FolderIcon) {
+                ((FolderIcon) child).setTextVisible(false);
+            }
+        } else {
+            // Show folder title if not in the hotseat
+            if (child instanceof FolderIcon) {
+                ((FolderIcon) child).setTextVisible(true);
+            }
+            layout = getScreenWithId(screenId);
+        }
+
+        ViewGroup.LayoutParams genericLp = child.getLayoutParams();
+        CellLayout.LayoutParams lp;
+        if (genericLp == null || !(genericLp instanceof CellLayout.LayoutParams)) {
+            lp = new CellLayout.LayoutParams(x, y, spanX, spanY);
+        } else {
+            lp = (CellLayout.LayoutParams) genericLp;
+            lp.cellX = x;
+            lp.cellY = y;
+            lp.cellHSpan = spanX;
+            lp.cellVSpan = spanY;
+        }
+
+        if (spanX < 0 && spanY < 0) {
+            lp.isLockedToGrid = false;
+        }
+
+        // Get the canonical child id to uniquely represent this view in this screen
+        ItemInfo info = (ItemInfo) child.getTag();
+        int childId = info.getViewId();
+
+        boolean markCellsAsOccupied = !(child instanceof Folder);
+        if (!layout.addViewToCellLayout(child, -1, childId, lp, markCellsAsOccupied)) {
+            // TODO: This branch occurs when the workspace is adding views
+            // outside of the defined grid
+            // maybe we should be deleting these items from the LauncherModel?
+            Log.e(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout");
+        }
+
+        child.setHapticFeedbackEnabled(false);
+        child.setOnLongClickListener(ItemLongClickListener.INSTANCE_WORKSPACE);
+        if (child instanceof DropTarget) {
+            onAddDropTarget((DropTarget) child);
+        }
+    }
+
+    Hotseat getHotseat();
+
+    CellLayout getScreenWithId(int screenId);
+
+    default void onAddDropTarget(DropTarget target) { }
+}
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index e734e70..40c6b5f 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -19,9 +19,11 @@
 import static com.android.launcher3.LauncherAnimUtils.DRAWABLE_ALPHA;
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
-import static com.android.launcher3.LauncherState.HOTSEAT_SEARCH_BOX;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_SCALE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_TRANSLATE;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE;
 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;
@@ -32,9 +34,11 @@
 import android.view.animation.Interpolator;
 
 import com.android.launcher3.LauncherState.PageAlphaProvider;
+import com.android.launcher3.LauncherState.ScaleAndTranslation;
 import com.android.launcher3.LauncherStateManager.AnimationConfig;
 import com.android.launcher3.anim.AnimatorSetBuilder;
 import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.graphics.WorkspaceAndHotseatScrim;
 
 /**
@@ -71,8 +75,10 @@
      */
     private void setWorkspaceProperty(LauncherState state, PropertySetter propertySetter,
             AnimatorSetBuilder builder, AnimationConfig config) {
-        float[] scaleAndTranslation = state.getWorkspaceScaleAndTranslation(mLauncher);
-        mNewScale = scaleAndTranslation[0];
+        ScaleAndTranslation scaleAndTranslation = state.getWorkspaceScaleAndTranslation(mLauncher);
+        ScaleAndTranslation hotseatScaleAndTranslation = state.getHotseatScaleAndTranslation(
+                mLauncher);
+        mNewScale = scaleAndTranslation.scale;
         PageAlphaProvider pageAlphaProvider = state.getWorkspacePageAlphaProvider(mLauncher);
         final int childCount = mWorkspace.getChildCount();
         for (int i = 0; i < childCount; i++) {
@@ -83,13 +89,30 @@
         int elements = state.getVisibleElements(mLauncher);
         Interpolator fadeInterpolator = builder.getInterpolator(ANIM_WORKSPACE_FADE,
                 pageAlphaProvider.interpolator);
-        boolean playAtomicComponent = config.playAtomicComponent();
+        boolean playAtomicComponent = config.playAtomicOverviewScaleComponent();
+        Hotseat hotseat = mWorkspace.getHotseat();
         if (playAtomicComponent) {
             Interpolator scaleInterpolator = builder.getInterpolator(ANIM_WORKSPACE_SCALE, ZOOM_OUT);
             propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, scaleInterpolator);
+
+            if (!hotseat.getRotationMode().isTransposed) {
+                // Set the hotseat's pivot point to match the workspace's, so that it scales together.
+                DragLayer dragLayer = mLauncher.getDragLayer();
+                float[] workspacePivot =
+                        new float[]{ mWorkspace.getPivotX(), mWorkspace.getPivotY() };
+                dragLayer.getDescendantCoordRelativeToSelf(mWorkspace, workspacePivot);
+                dragLayer.mapCoordInSelfToDescendant(hotseat, workspacePivot);
+                hotseat.setPivotX(workspacePivot[0]);
+                hotseat.setPivotY(workspacePivot[1]);
+            }
+            float hotseatScale = hotseatScaleAndTranslation.scale;
+            Interpolator hotseatScaleInterpolator = builder.getInterpolator(ANIM_HOTSEAT_SCALE,
+                    scaleInterpolator);
+            propertySetter.setFloat(hotseat, SCALE_PROPERTY, hotseatScale,
+                    hotseatScaleInterpolator);
+
             float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0;
-            propertySetter.setViewAlpha(mLauncher.getHotseat().getLayout(), hotseatIconsAlpha,
-                    fadeInterpolator);
+            propertySetter.setViewAlpha(hotseat, hotseatIconsAlpha, fadeInterpolator);
             propertySetter.setViewAlpha(mLauncher.getWorkspace().getPageIndicator(),
                     hotseatIconsAlpha, fadeInterpolator);
         }
@@ -99,16 +122,25 @@
             return;
         }
 
-        Interpolator translationInterpolator = !playAtomicComponent ? LINEAR : ZOOM_OUT;
+        Interpolator translationInterpolator = !playAtomicComponent
+                ? LINEAR
+                : builder.getInterpolator(ANIM_WORKSPACE_TRANSLATE, ZOOM_OUT);
         propertySetter.setFloat(mWorkspace, View.TRANSLATION_X,
-                scaleAndTranslation[1], translationInterpolator);
+                scaleAndTranslation.translationX, translationInterpolator);
         propertySetter.setFloat(mWorkspace, View.TRANSLATION_Y,
-                scaleAndTranslation[2], translationInterpolator);
+                scaleAndTranslation.translationY, translationInterpolator);
 
-        propertySetter.setViewAlpha(mLauncher.getHotseatSearchBox(),
-                (elements & HOTSEAT_SEARCH_BOX) != 0 ? 1 : 0, fadeInterpolator);
+        Interpolator hotseatTranslationInterpolator = builder.getInterpolator(
+                ANIM_HOTSEAT_TRANSLATE, translationInterpolator);
+        propertySetter.setFloat(hotseat, View.TRANSLATION_Y,
+                hotseatScaleAndTranslation.translationY, hotseatTranslationInterpolator);
+        propertySetter.setFloat(mWorkspace.getPageIndicator(), View.TRANSLATION_Y,
+                hotseatScaleAndTranslation.translationY, hotseatTranslationInterpolator);
 
-        // Set scrim
+        setScrim(propertySetter, state);
+    }
+
+    public void setScrim(PropertySetter propertySetter, LauncherState state) {
         WorkspaceAndHotseatScrim scrim = mLauncher.getDragLayer().getScrim();
         propertySetter.setFloat(scrim, SCRIM_PROGRESS, state.getWorkspaceScrimAlpha(mLauncher),
                 LINEAR);
@@ -130,7 +162,7 @@
             propertySetter.setInt(cl.getScrimBackground(),
                     DRAWABLE_ALPHA, drawableAlpha, ZOOM_OUT);
         }
-        if (config.playAtomicComponent()) {
+        if (config.playAtomicOverviewScaleComponent()) {
             Interpolator fadeInterpolator = builder.getInterpolator(ANIM_WORKSPACE_FADE,
                     pageAlphaProvider.interpolator);
             propertySetter.setFloat(cl.getShortcutsAndWidgets(), View.ALPHA,
diff --git a/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java
index bd3bb4d..117296d 100644
--- a/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java
@@ -19,8 +19,6 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.os.Bundle;
-import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
-import android.support.v4.widget.ExploreByTouchHelper;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.accessibility.AccessibilityEvent;
@@ -31,6 +29,9 @@
 
 import java.util.List;
 
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
+import androidx.customview.widget.ExploreByTouchHelper;
+
 /**
  * Helper class to make drag-and-drop in a {@link CellLayout} accessible.
  */
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 81a0e1d..fd4df52 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -30,7 +30,7 @@
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.PendingAddItemInfo;
 import com.android.launcher3.R;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.dragndrop.DragController.DragListener;
 import com.android.launcher3.dragndrop.DragOptions;
@@ -39,6 +39,7 @@
 import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.touch.ItemLongClickListener;
+import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
 
@@ -126,9 +127,7 @@
         }
 
         // Do not add move actions for keyboard request as this uses virtual nodes.
-        if (!fromKeyboard && ((item instanceof ShortcutInfo)
-                || (item instanceof LauncherAppWidgetInfo)
-                || (item instanceof FolderInfo))) {
+        if (!fromKeyboard && itemSupportsAccessibleDrag(item)) {
             info.addAction(mActions.get(MOVE));
 
             if (item.container >= 0) {
@@ -145,6 +144,15 @@
         }
     }
 
+    private boolean itemSupportsAccessibleDrag(ItemInfo item) {
+        if (item instanceof WorkspaceItemInfo) {
+            // Support the action unless the item is in a context menu.
+            return item.screenId >= 0;
+        }
+        return (item instanceof LauncherAppWidgetInfo)
+                || (item instanceof FolderInfo);
+    }
+
     @Override
     public boolean performAccessibilityAction(View host, int action, Bundle args) {
         if ((host.getTag() instanceof ItemInfo)
@@ -159,13 +167,13 @@
             beginAccessibleDrag(host, item);
         } else if (action == ADD_TO_WORKSPACE) {
             final int[] coordinates = new int[2];
-            final long screenId = findSpaceOnWorkspace(item, coordinates);
+            final int screenId = findSpaceOnWorkspace(item, coordinates);
             mLauncher.getStateManager().goToState(NORMAL, true, new Runnable() {
 
                 @Override
                 public void run() {
                     if (item instanceof AppInfo) {
-                        ShortcutInfo info = ((AppInfo) item).makeShortcut();
+                        WorkspaceItemInfo info = ((AppInfo) item).makeWorkspaceItem();
                         mLauncher.getModelWriter().addItemToDatabase(info,
                                 Favorites.CONTAINER_DESKTOP,
                                 screenId, coordinates[0], coordinates[1]);
@@ -187,11 +195,11 @@
         } else if (action == MOVE_TO_WORKSPACE) {
             Folder folder = Folder.getOpen(mLauncher);
             folder.close(true);
-            ShortcutInfo info = (ShortcutInfo) item;
+            WorkspaceItemInfo info = (WorkspaceItemInfo) item;
             folder.getInfo().remove(info, false);
 
             final int[] coordinates = new int[2];
-            final long screenId = findSpaceOnWorkspace(item, coordinates);
+            final int screenId = findSpaceOnWorkspace(item, coordinates);
             mLauncher.getModelWriter().moveItemInDatabase(info,
                     LauncherSettings.Favorites.CONTAINER_DESKTOP,
                     screenId, coordinates[0], coordinates[1]);
@@ -210,7 +218,7 @@
             });
         } else if (action == RESIZE) {
             final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) item;
-            final ArrayList<Integer> actions = getSupportedResizeActions(host, info);
+            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));
@@ -242,8 +250,8 @@
         return false;
     }
 
-    private ArrayList<Integer> getSupportedResizeActions(View host, LauncherAppWidgetInfo info) {
-        ArrayList<Integer> actions = new ArrayList<>();
+    private IntArray getSupportedResizeActions(View host, LauncherAppWidgetInfo info) {
+        IntArray actions = new IntArray();
 
         AppWidgetProviderInfo providerInfo = ((LauncherAppWidgetHostView) host).getAppWidgetInfo();
         if (providerInfo == null) {
@@ -392,10 +400,10 @@
     /**
      * Find empty space on the workspace and returns the screenId.
      */
-    protected long findSpaceOnWorkspace(ItemInfo info, int[] outCoordinates) {
+    protected int findSpaceOnWorkspace(ItemInfo info, int[] outCoordinates) {
         Workspace workspace = mLauncher.getWorkspace();
-        ArrayList<Long> workspaceScreens = workspace.getScreenOrder();
-        long screenId;
+        IntArray workspaceScreens = workspace.getScreenOrder();
+        int screenId;
 
         // First check if there is space on the current screen.
         int screenIndex = workspace.getCurrentPage();
diff --git a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
index cfb0520..c0c0b37 100644
--- a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
@@ -27,7 +27,7 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.notification.NotificationMainView;
 import com.android.launcher3.shortcuts.DeepShortcutView;
 
@@ -64,9 +64,9 @@
             if (!(host.getParent() instanceof DeepShortcutView)) {
                 return false;
             }
-            final ShortcutInfo info = ((DeepShortcutView) host.getParent()).getFinalInfo();
+            final WorkspaceItemInfo info = ((DeepShortcutView) host.getParent()).getFinalInfo();
             final int[] coordinates = new int[2];
-            final long screenId = findSpaceOnWorkspace(item, coordinates);
+            final int screenId = findSpaceOnWorkspace(item, coordinates);
             Runnable onComplete = new Runnable() {
                 @Override
                 public void run() {
diff --git a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
index e6f120f..17daeb8 100644
--- a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
+++ b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
@@ -18,7 +18,6 @@
 
 import android.content.Context;
 import android.graphics.Rect;
-import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
 import android.text.TextUtils;
 import android.view.View;
 
@@ -28,10 +27,12 @@
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DragType;
 import com.android.launcher3.dragndrop.DragLayer;
 
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
+
 /**
  * Implementation of {@link DragAndDropAccessibilityDelegate} to support DnD on workspace.
  */
@@ -105,7 +106,7 @@
                 // For icons, we can consider cells that have another icon or a folder.
                 ItemInfo info = (ItemInfo) child.getTag();
                 if (info instanceof AppInfo || info instanceof FolderInfo ||
-                        info instanceof ShortcutInfo) {
+                        info instanceof WorkspaceItemInfo) {
                     return id;
                 }
             }
@@ -124,7 +125,7 @@
             return mContext.getString(R.string.item_moved);
         } else {
             ItemInfo info = (ItemInfo) child.getTag();
-            if (info instanceof AppInfo || info instanceof ShortcutInfo) {
+            if (info instanceof AppInfo || info instanceof WorkspaceItemInfo) {
                 return mContext.getString(R.string.folder_created);
 
             } else if (info instanceof FolderInfo) {
@@ -169,14 +170,14 @@
 
     public static String getDescriptionForDropOver(View overChild, Context context) {
         ItemInfo info = (ItemInfo) overChild.getTag();
-        if (info instanceof ShortcutInfo) {
+        if (info instanceof WorkspaceItemInfo) {
             return context.getString(R.string.create_folder_with, info.title);
         } else if (info instanceof FolderInfo) {
             if (TextUtils.isEmpty(info.title)) {
                 // Find the first item in the folder.
                 FolderInfo folder = (FolderInfo) info;
-                ShortcutInfo firstItem = null;
-                for (ShortcutInfo shortcut : folder.contents) {
+                WorkspaceItemInfo firstItem = null;
+                for (WorkspaceItemInfo shortcut : folder.contents) {
                     if (firstItem == null || firstItem.rank > shortcut.rank) {
                         firstItem = shortcut;
                     }
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index fdf32af..ea9b077 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -21,21 +21,18 @@
 import android.graphics.Paint;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.os.Bundle;
 import android.os.Process;
-import android.support.animation.DynamicAnimation;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.StringRes;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
 import android.text.Selection;
 import android.text.SpannableStringBuilder;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.WindowInsets;
 
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.DeviceProfile;
@@ -46,17 +43,29 @@
 import com.android.launcher3.InsettableFrameLayout;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.compat.AccessibilityManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.keyboard.FocusedItemDecorator;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 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.BottomUserEducationView;
 import com.android.launcher3.views.RecyclerViewFastScroller;
 import com.android.launcher3.views.SpringRelativeLayout;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+import androidx.dynamicanimation.animation.DynamicAnimation;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
 /**
  * The all apps view container.
  */
@@ -66,6 +75,7 @@
     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 final Launcher mLauncher;
     private final AdapterHolder[] mAH;
@@ -89,6 +99,8 @@
     private RecyclerViewFastScroller mTouchHandler;
     private final Point mFastScrollerOffset = new Point();
 
+    private final MultiValueAlpha mMultiValueAlpha;
+
     public AllAppsContainerView(Context context) {
         this(context, null);
     }
@@ -118,12 +130,18 @@
         addSpringView(R.id.all_apps_header);
         addSpringView(R.id.apps_list_view);
         addSpringView(R.id.all_apps_tabs_view_pager);
+
+        mMultiValueAlpha = new MultiValueAlpha(this, ALPHA_CHANNEL_COUNT);
     }
 
     public AllAppsStore getAppsStore() {
         return mAllAppsStore;
     }
 
+    public AlphaProperty getAlphaProperty(int index) {
+        return mMultiValueAlpha.getProperty(index);
+    }
+
     @Override
     protected void setDampedScrollShift(float shift) {
         // Bound the shift amount to avoid content from drawing on top (Y-val) of the QSB.
@@ -178,11 +196,18 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
+
+        // The AllAppsContainerView houses the QSB and is hence visible from the Workspace
+        // Overview states. We shouldn't intercept for the scrubber in these cases.
+        if (!mLauncher.isInState(LauncherState.ALL_APPS)) return false;
+
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
             AllAppsRecyclerView rv = getActiveRecyclerView();
             if (rv != null &&
                     rv.getScrollbar().isHitInParent(ev.getX(), ev.getY(), mFastScrollerOffset)) {
                 mTouchHandler = rv.getScrollbar();
+            } else {
+                mTouchHandler = null;
             }
         }
         if (mTouchHandler != null) {
@@ -298,8 +323,19 @@
         }
         setLayoutParams(mlp);
 
-        mNavBarScrimHeight = insets.bottom;
         InsettableFrameLayout.dispatchInsets(this, insets);
+        mLauncher.getAllAppsController()
+                .setScrollRangeDelta(mSearchUiManager.getScrollRangeDelta(insets));
+    }
+
+    @Override
+    public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
+        if (Utilities.ATLEAST_Q) {
+            mNavBarScrimHeight = insets.getTappableElementInsets().bottom;
+        } else {
+            mNavBarScrimHeight = insets.getStableInsetBottom();
+        }
+        return super.dispatchApplyWindowInsets(insets);
     }
 
     @Override
@@ -388,6 +424,26 @@
         }
     }
 
+    // Used by tests only
+    private boolean isDescendantViewVisible(int viewId) {
+        final View view = findViewById(viewId);
+        if (view == null) return false;
+
+        if (!view.isShown()) return false;
+
+        return view.getGlobalVisibleRect(new Rect());
+    }
+
+    // Used by tests only
+    public boolean isPersonalTabVisible() {
+        return isDescendantViewVisible(R.id.tab_personal);
+    }
+
+    // Used by tests only
+    public boolean isWorkTabVisible() {
+        return isDescendantViewVisible(R.id.tab_work);
+    }
+
     public AlphabeticalAppsList getApps() {
         return mAH[AdapterHolder.MAIN].appsList;
     }
@@ -462,8 +518,13 @@
     }
 
     public void onScrollUpEnd() {
+        highlightWorkTabIfNecessary();
+    }
+
+    void highlightWorkTabIfNecessary() {
         if (mUsingTabs) {
-            ((PersonalWorkSlidingTabStrip) findViewById(R.id.tabs)).highlightWorkTabIfNecessary();
+            ((PersonalWorkSlidingTabStrip) findViewById(R.id.tabs))
+                    .highlightWorkTabIfNecessary();
         }
     }
 
@@ -549,4 +610,36 @@
                     && verticalFadingEdge);
         }
     }
+
+    @Override
+    public boolean performAccessibilityAction(int action, Bundle arguments) {
+        if (AccessibilityManagerCompat.processTestRequest(
+                mLauncher, TestProtocol.GET_SCROLL_MESSAGE, action, arguments,
+                response ->
+                        response.putInt(TestProtocol.SCROLL_Y_FIELD,
+                                getActiveRecyclerView().getCurrentScrollY()))) {
+            return true;
+        }
+
+        return super.performAccessibilityAction(action, arguments);
+    }
+
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent ev) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_START_TAG, "AllAppsContainerView.dispatchTouchEvent " + ev);
+        }
+        final boolean result = super.dispatchTouchEvent(ev);
+        switch (ev.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN:
+                if (result) mAllAppsStore.enableDeferUpdates(
+                        AllAppsStore.DEFER_UPDATES_USER_INTERACTION);
+                break;
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                mAllAppsStore.disableDeferUpdates(AllAppsStore.DEFER_UPDATES_USER_INTERACTION);
+                break;
+        }
+        return result;
+    }
 }
diff --git a/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java b/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java
index e08cb15..3ee1293 100644
--- a/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java
+++ b/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java
@@ -15,13 +15,13 @@
  */
 package com.android.launcher3.allapps;
 
-import android.support.v7.widget.RecyclerView;
-
 import com.android.launcher3.util.Thunk;
 
 import java.util.HashSet;
 import java.util.List;
 
+import androidx.recyclerview.widget.RecyclerView;
+
 public class AllAppsFastScrollHelper implements AllAppsGridAdapter.BindViewCallback {
 
     private static final int INITIAL_TOUCH_SETTLING_DURATION = 100;
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 27fc53a..3cfa0b1 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -18,16 +18,10 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
-import android.support.v4.view.accessibility.AccessibilityEventCompat;
-import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
-import android.support.v4.view.accessibility.AccessibilityRecordCompat;
-import android.support.v7.widget.GridLayoutManager;
-import android.support.v7.widget.RecyclerView;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnFocusChangeListener;
-import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.TextView;
@@ -38,12 +32,19 @@
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.AlphabeticalAppsList.AdapterItem;
 import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.model.AppLaunchTracker;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.touch.ItemLongClickListener;
 import com.android.launcher3.util.PackageManagerHelper;
 
 import java.util.List;
 
+import androidx.core.view.accessibility.AccessibilityEventCompat;
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
+import androidx.core.view.accessibility.AccessibilityRecordCompat;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
 /**
  * The grid view adapter of all the apps.
  */
@@ -251,7 +252,7 @@
                         R.layout.all_apps_icon, parent, false);
                 icon.setOnClickListener(ItemClickHandler.INSTANCE);
                 icon.setOnLongClickListener(ItemLongClickListener.INSTANCE_ALL_APPS);
-                icon.setLongPressTimeout(ViewConfiguration.getLongPressTimeout());
+                icon.setLongPressTimeoutFactor(1f);
                 icon.setOnFocusChangeListener(mIconFocusListener);
 
                 // Ensure the all apps icon height matches the workspace icons in portrait mode.
@@ -263,12 +264,8 @@
             case VIEW_TYPE_SEARCH_MARKET:
                 View searchMarketView = mLayoutInflater.inflate(R.layout.all_apps_search_market,
                         parent, false);
-                searchMarketView.setOnClickListener(new View.OnClickListener() {
-                    @Override
-                    public void onClick(View v) {
-                        mLauncher.startActivitySafely(v, mMarketSearchIntent, null);
-                    }
-                });
+                searchMarketView.setOnClickListener(v -> mLauncher.startActivitySafely(
+                        v, mMarketSearchIntent, null, AppLaunchTracker.CONTAINER_SEARCH));
                 return new ViewHolder(searchMarketView);
             case VIEW_TYPE_ALL_APPS_DIVIDER:
                 return new ViewHolder(mLayoutInflater.inflate(
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index a6c1346..a0e9dc5 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -21,7 +21,6 @@
 import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
-import android.support.v7.widget.RecyclerView;
 import android.util.AttributeSet;
 import android.util.SparseIntArray;
 import android.view.MotionEvent;
@@ -33,14 +32,17 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
-import com.android.launcher3.graphics.DrawableFactory;
-import com.android.launcher3.logging.UserEventDispatcher.LogContainerProvider;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.compat.AccessibilityManagerCompat;
+import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.views.RecyclerViewFastScroller;
 
 import java.util.List;
 
+import androidx.recyclerview.widget.RecyclerView;
+
 /**
  * A RecyclerView with custom fast scroll support for the all apps view.
  */
@@ -151,8 +153,7 @@
 
         if (mApps.hasNoFilteredResults()) {
             if (mEmptySearchBackground == null) {
-                mEmptySearchBackground = DrawableFactory.get(getContext())
-                        .getAllAppsBackground(getContext());
+                mEmptySearchBackground = new AllAppsBackgroundDrawable(getContext());
                 mEmptySearchBackground.setAlpha(0);
                 mEmptySearchBackground.setCallback(this);
                 updateEmptySearchBackgroundBounds();
@@ -419,4 +420,13 @@
     public boolean hasOverlappingRendering() {
         return false;
     }
+
+    @Override
+    public void onScrollStateChanged(int state) {
+        super.onScrollStateChanged(state);
+
+        if (state == SCROLL_STATE_IDLE) {
+            AccessibilityManagerCompat.sendScrollFinishedEventToTest(getContext());
+        }
+    }
 }
diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java
index dc34892..160042e 100644
--- a/src/com/android/launcher3/allapps/AllAppsStore.java
+++ b/src/com/android/launcher3/allapps/AllAppsStore.java
@@ -29,19 +29,27 @@
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
-import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
 
 /**
  * A utility class to maintain the collection of all apps.
  */
 public class AllAppsStore {
 
+    // Defer updates flag used to defer all apps updates to the next draw.
+    public static final int DEFER_UPDATES_NEXT_DRAW = 1 << 0;
+    // Defer updates flag used to defer all apps updates while the user interacts with all apps.
+    public static final int DEFER_UPDATES_USER_INTERACTION = 1 << 1;
+    // Defer updates flag used to defer all apps updates by a test's request.
+    public static final int DEFER_UPDATES_TEST = 1 << 2;
+
     private PackageUserKey mTempKey = new PackageUserKey(null, null);
     private final HashMap<ComponentKey, AppInfo> mComponentToAppMap = new HashMap<>();
     private final List<OnUpdateListener> mUpdateListeners = new ArrayList<>();
     private final ArrayList<ViewGroup> mIconContainers = new ArrayList<>();
 
-    private boolean mDeferUpdates = false;
+    private int mDeferUpdatesFlags = 0;
     private boolean mUpdatePending = false;
 
     public Collection<AppInfo> getApps() {
@@ -60,17 +68,22 @@
         return mComponentToAppMap.get(key);
     }
 
-    public void setDeferUpdates(boolean deferUpdates) {
-        if (mDeferUpdates != deferUpdates) {
-            mDeferUpdates = deferUpdates;
+    public void enableDeferUpdates(int flag) {
+        mDeferUpdatesFlags |= flag;
+    }
 
-            if (!mDeferUpdates && mUpdatePending) {
-                notifyUpdate();
-                mUpdatePending = false;
-            }
+    public void disableDeferUpdates(int flag) {
+        mDeferUpdatesFlags &= ~flag;
+        if (mDeferUpdatesFlags == 0 && mUpdatePending) {
+            notifyUpdate();
+            mUpdatePending = false;
         }
     }
 
+    public int getDeferUpdatesFlags() {
+        return mDeferUpdatesFlags;
+    }
+
     /**
      * Adds or updates existing apps in the list
      */
@@ -93,7 +106,7 @@
 
 
     private void notifyUpdate() {
-        if (mDeferUpdates) {
+        if (mDeferUpdatesFlags != 0) {
             mUpdatePending = true;
             return;
         }
@@ -121,12 +134,12 @@
         mIconContainers.remove(container);
     }
 
-    public void updateIconBadges(Set<PackageUserKey> updatedBadges) {
+    public void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
         updateAllIcons((child) -> {
             if (child.getTag() instanceof ItemInfo) {
                 ItemInfo info = (ItemInfo) child.getTag();
-                if (mTempKey.updateFromItemInfo(info) && updatedBadges.contains(mTempKey)) {
-                    child.applyBadgeState(info, true /* animate */);
+                if (mTempKey.updateFromItemInfo(info) && updatedDots.test(mTempKey)) {
+                    child.applyDotState(info, true /* animate */);
                 }
             }
         });
@@ -140,7 +153,7 @@
         });
     }
 
-    private void updateAllIcons(IconAction action) {
+    private void updateAllIcons(Consumer<BubbleTextView> action) {
         for (int i = mIconContainers.size() - 1; i >= 0; i--) {
             ViewGroup parent = mIconContainers.get(i);
             int childCount = parent.getChildCount();
@@ -148,7 +161,7 @@
             for (int j = 0; j < childCount; j++) {
                 View child = parent.getChildAt(j);
                 if (child instanceof BubbleTextView) {
-                    action.apply((BubbleTextView) child);
+                    action.accept((BubbleTextView) child);
                 }
             }
         }
@@ -157,8 +170,4 @@
     public interface OnUpdateListener {
         void onAppsUpdated();
     }
-
-    public interface IconAction {
-        void apply(BubbleTextView icon);
-    }
 }
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 24a8d51..4683893 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -1,8 +1,9 @@
 package com.android.launcher3.allapps;
 
 import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT;
-import static com.android.launcher3.LauncherState.ALL_APPS_HEADER;
 import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA;
+import static com.android.launcher3.LauncherState.BACKGROUND_APP;
+import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.LauncherState.VERTICAL_SWIPE_INDICATOR;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE;
@@ -15,9 +16,8 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.util.Property;
-import android.view.View;
+import android.util.FloatProperty;
+import android.util.Log;
 import android.view.animation.Interpolator;
 
 import com.android.launcher3.DeviceProfile;
@@ -30,6 +30,8 @@
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorSetBuilder;
 import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.anim.SpringObjectAnimator;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.ScrimView;
 
@@ -45,8 +47,11 @@
  */
 public class AllAppsTransitionController implements StateHandler, OnDeviceProfileChangeListener {
 
-    public static final Property<AllAppsTransitionController, Float> ALL_APPS_PROGRESS =
-            new Property<AllAppsTransitionController, Float>(Float.class, "allAppsProgress") {
+    private static final float SPRING_DAMPING_RATIO = 0.9f;
+    private static final float SPRING_STIFFNESS = 600f;
+
+    public static final FloatProperty<AllAppsTransitionController> ALL_APPS_PROGRESS =
+            new FloatProperty<AllAppsTransitionController>("allAppsProgress") {
 
         @Override
         public Float get(AllAppsTransitionController controller) {
@@ -54,11 +59,13 @@
         }
 
         @Override
-        public void set(AllAppsTransitionController controller, Float progress) {
+        public void setValue(AllAppsTransitionController controller, float progress) {
             controller.setProgress(progress);
         }
     };
 
+    private static final int APPS_VIEW_ALPHA_CHANNEL_INDEX = 0;
+
     private AllAppsContainerView mAppsView;
     private ScrimView mScrimView;
 
@@ -91,18 +98,13 @@
         return mShiftRange;
     }
 
-    private void onProgressAnimationStart() {
-        // Initialize values that should not change until #onDragEnd
-        mAppsView.setVisibility(View.VISIBLE);
-    }
-
     @Override
     public void onDeviceProfileChanged(DeviceProfile dp) {
         mIsVerticalLayout = dp.isVerticalBarLayout();
         setScrollRangeDelta(mScrollRangeDelta);
 
         if (mIsVerticalLayout) {
-            mAppsView.setAlpha(1);
+            mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(1);
             mLauncher.getHotseat().setTranslationY(0);
             mLauncher.getWorkspace().getPageIndicator().setTranslationY(0);
         }
@@ -123,12 +125,6 @@
         float shiftCurrent = progress * mShiftRange;
 
         mAppsView.setTranslationY(shiftCurrent);
-        float hotseatTranslation = -mShiftRange + shiftCurrent;
-
-        if (!mIsVerticalLayout) {
-            mLauncher.getHotseat().setTranslationY(hotseatTranslation);
-            mLauncher.getWorkspace().getPageIndicator().setTranslationY(hotseatTranslation);
-        }
 
         // Use a light system UI (dark icons) if all apps is behind at least half of the
         // status bar.
@@ -139,6 +135,15 @@
         } else {
             mLauncher.getSystemUiController().updateUiState(UI_STATE_ALL_APPS, 0);
         }
+
+        if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
+            // Translate hotseat with the shelf until reaching overview.
+            float overviewProgress = OVERVIEW.getVerticalProgress(mLauncher);
+            if (progress >= overviewProgress || mLauncher.isInState(BACKGROUND_APP)) {
+                float hotseatShift = (progress - overviewProgress) * mShiftRange;
+                mLauncher.getHotseat().setTranslationY(hotseatShift);
+            }
+        }
     }
 
     public float getProgress() {
@@ -163,6 +168,10 @@
     @Override
     public void setStateWithAnimation(LauncherState toState,
             AnimatorSetBuilder builder, AnimationConfig config) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG,
+                    "setStateWithAnimation " + toState.getClass().getSimpleName());
+        }
         float targetProgress = toState.getVerticalProgress(mLauncher);
         if (Float.compare(mProgress, targetProgress) == 0) {
             setAlphas(toState, config, builder);
@@ -179,8 +188,7 @@
         Interpolator interpolator = config.userControlled ? LINEAR : toState == OVERVIEW
                 ? builder.getInterpolator(ANIM_OVERVIEW_SCALE, FAST_OUT_SLOW_IN)
                 : FAST_OUT_SLOW_IN;
-        ObjectAnimator anim =
-                ObjectAnimator.ofFloat(this, ALL_APPS_PROGRESS, mProgress, targetProgress);
+        Animator anim = createSpringAnimation(mProgress, targetProgress);
         anim.setDuration(config.duration);
         anim.setInterpolator(builder.getInterpolator(ANIM_VERTICAL_PROGRESS, interpolator));
         anim.addListener(getProgressAnimatorListener());
@@ -190,24 +198,31 @@
         setAlphas(toState, config, builder);
     }
 
+    public Animator createSpringAnimation(float... progressValues) {
+        return new SpringObjectAnimator<>(this, ALL_APPS_PROGRESS, 1f / mShiftRange,
+                SPRING_DAMPING_RATIO, SPRING_STIFFNESS, progressValues);
+    }
+
     private void setAlphas(LauncherState toState, AnimationConfig config,
             AnimatorSetBuilder builder) {
+        setAlphas(toState.getVisibleElements(mLauncher), config, builder);
+    }
+
+    public void setAlphas(int visibleElements, AnimationConfig config, AnimatorSetBuilder builder) {
         PropertySetter setter = config == null ? NO_ANIM_PROPERTY_SETTER
                 : config.getPropertySetter(builder);
-        int visibleElements = toState.getVisibleElements(mLauncher);
-        boolean hasHeader = (visibleElements & ALL_APPS_HEADER) != 0;
         boolean hasHeaderExtra = (visibleElements & ALL_APPS_HEADER_EXTRA) != 0;
         boolean hasContent = (visibleElements & ALL_APPS_CONTENT) != 0;
 
         Interpolator allAppsFade = builder.getInterpolator(ANIM_ALL_APPS_FADE, LINEAR);
-        setter.setViewAlpha(mAppsView.getSearchView(), hasHeader ? 1 : 0, allAppsFade);
         setter.setViewAlpha(mAppsView.getContentView(), hasContent ? 1 : 0, allAppsFade);
         setter.setViewAlpha(mAppsView.getScrollBar(), hasContent ? 1 : 0, allAppsFade);
         mAppsView.getFloatingHeaderView().setContentVisibility(hasHeaderExtra, hasContent, setter,
                 allAppsFade);
+        mAppsView.getSearchUiManager().setContentVisibility(visibleElements, setter, allAppsFade);
 
         setter.setInt(mScrimView, ScrimView.DRAG_HANDLE_ALPHA,
-                (visibleElements & VERTICAL_SWIPE_INDICATOR) != 0 ? 255 : 0, LINEAR);
+                (visibleElements & VERTICAL_SWIPE_INDICATOR) != 0 ? 255 : 0, allAppsFade);
     }
 
     public AnimatorListenerAdapter getProgressAnimatorListener() {
@@ -216,11 +231,6 @@
             public void onAnimationSuccess(Animator animator) {
                 onProgressAnimationEnd();
             }
-
-            @Override
-            public void onAnimationStart(Animator animation) {
-                onProgressAnimationStart();
-            }
         };
     }
 
@@ -232,7 +242,7 @@
     /**
      * Updates the total scroll range but does not update the UI.
      */
-    public void setScrollRangeDelta(float delta) {
+    void setScrollRangeDelta(float delta) {
         mScrollRangeDelta = delta;
         mShiftRange = mLauncher.getDeviceProfile().heightPx - mScrollRangeDelta;
 
@@ -247,13 +257,19 @@
      */
     private void onProgressAnimationEnd() {
         if (Float.compare(mProgress, 1f) == 0) {
-            mAppsView.setVisibility(View.INVISIBLE);
             mAppsView.reset(false /* animate */);
-        } else if (Float.compare(mProgress, 0f) == 0) {
-            mAppsView.setVisibility(View.VISIBLE);
+        } else if (isAllAppsExpanded()) {
             mAppsView.onScrollUpEnd();
-        } else {
-            mAppsView.setVisibility(View.VISIBLE);
+        }
+    }
+
+    private boolean isAllAppsExpanded() {
+        return Float.compare(mProgress, 0f) == 0;
+    }
+
+    public void highlightWorkTabIfNecessary() {
+        if (isAllAppsExpanded()) {
+            mAppsView.highlightWorkTabIfNecessary();
         }
     }
 }
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 434918d..2ad92e1 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -23,6 +23,7 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.AlphabeticIndexCompat;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.LabelComparator;
@@ -300,6 +301,11 @@
     }
 
     private void refreshRecyclerView() {
+        if (TestProtocol.sDebugTracing) {
+            android.util.Log.d(TestProtocol.NO_START_TAG,
+                    "refreshRecyclerView @ " + android.util.Log.getStackTraceString(
+                            new Throwable()));
+        }
         if (mAdapter != null) {
             mAdapter.notifyDataSetChanged();
         }
diff --git a/src/com/android/launcher3/allapps/DiscoveryBounce.java b/src/com/android/launcher3/allapps/DiscoveryBounce.java
index 3c3c406..8c59626 100644
--- a/src/com/android/launcher3/allapps/DiscoveryBounce.java
+++ b/src/com/android/launcher3/allapps/DiscoveryBounce.java
@@ -24,13 +24,17 @@
 import android.animation.Animator;
 import android.animation.AnimatorInflater;
 import android.animation.AnimatorListenerAdapter;
-import android.app.ActivityManager;
+import android.content.SharedPreferences;
 import android.os.Handler;
 import android.view.MotionEvent;
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager;
+import com.android.launcher3.LauncherStateManager.StateListener;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.states.InternalStateHandler;
 
@@ -43,10 +47,24 @@
 
     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 int BOUNCE_MAX_COUNT = 3;
 
     private final Launcher mLauncher;
     private final Animator mDiscoBounceAnimation;
 
+    private final StateListener mStateListener = new StateListener() {
+        @Override
+        public void onStateTransitionStart(LauncherState toState) {
+            handleClose(false);
+        }
+
+        @Override
+        public void onStateTransitionComplete(LauncherState finalState) {}
+    };
+
     public DiscoveryBounce(Launcher launcher, float delta) {
         super(launcher, null);
         mLauncher = launcher;
@@ -62,6 +80,7 @@
             }
         });
         mDiscoBounceAnimation.addListener(controller.getProgressAnimatorListener());
+        launcher.getStateManager().addStateListener(mStateListener);
     }
 
     @Override
@@ -100,6 +119,7 @@
             // Reset the all-apps progress to what ever it was previously.
             mLauncher.getAllAppsController().setProgress(mLauncher.getStateManager()
                     .getState().getVerticalProgress(mLauncher));
+            mLauncher.getStateManager().removeStateListener(mStateListener);
         }
     }
 
@@ -125,10 +145,11 @@
 
     private static void showForHomeIfNeeded(Launcher launcher, boolean withDelay) {
         if (!launcher.isInState(NORMAL)
-                || launcher.getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)
+                || (launcher.getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)
+                && !shouldShowForWorkProfile(launcher))
                 || AbstractFloatingView.getTopOpenView(launcher) != null
                 || UserManagerCompat.getInstance(launcher).isDemoUser()
-                || ActivityManager.isRunningInTestHarness()) {
+                || Utilities.IS_RUNNING_IN_TEST_HARNESS) {
             return;
         }
 
@@ -136,6 +157,7 @@
             new Handler().postDelayed(() -> showForHomeIfNeeded(launcher, false), DELAY_MS);
             return;
         }
+        incrementHomeBounceCount(launcher);
 
         new DiscoveryBounce(launcher, 0).show(HOTSEAT);
     }
@@ -149,9 +171,10 @@
                 || !launcher.hasBeenResumed()
                 || launcher.isForceInvisible()
                 || launcher.getDeviceProfile().isVerticalBarLayout()
-                || launcher.getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false)
+                || (launcher.getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false)
+                && !shouldShowForWorkProfile(launcher))
                 || UserManagerCompat.getInstance(launcher).isDemoUser()
-                || ActivityManager.isRunningInTestHarness()) {
+                || Utilities.IS_RUNNING_IN_TEST_HARNESS) {
             return;
         }
 
@@ -163,6 +186,7 @@
             // TODO: Move these checks to the top and call this method after invalidate handler.
             return;
         }
+        incrementShelfBounceCount(launcher);
 
         new DiscoveryBounce(launcher, (1 - OVERVIEW.getVerticalProgress(launcher)))
                 .show(PREDICTION);
@@ -189,4 +213,28 @@
             mController.setProgress(progress - mDelta);
         }
     }
+
+    private static boolean shouldShowForWorkProfile(Launcher launcher) {
+        return !launcher.getSharedPrefs().getBoolean(
+                PersonalWorkSlidingTabStrip.KEY_SHOWED_PEEK_WORK_TAB, false)
+                && UserManagerCompat.getInstance(launcher).hasWorkProfile();
+    }
+
+    private static void incrementShelfBounceCount(Launcher launcher) {
+        SharedPreferences sharedPrefs = launcher.getSharedPrefs();
+        int count = sharedPrefs.getInt(SHELF_BOUNCE_COUNT, 0);
+        if (count > BOUNCE_MAX_COUNT) {
+            return;
+        }
+        sharedPrefs.edit().putInt(SHELF_BOUNCE_COUNT, count + 1).apply();
+    }
+
+    private static void incrementHomeBounceCount(Launcher launcher) {
+        SharedPreferences sharedPrefs = launcher.getSharedPrefs();
+        int count = sharedPrefs.getInt(HOME_BOUNCE_COUNT, 0);
+        if (count > BOUNCE_MAX_COUNT) {
+            return;
+        }
+        sharedPrefs.edit().putInt(HOME_BOUNCE_COUNT, count + 1).apply();
+    }
 }
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderRow.java b/src/com/android/launcher3/allapps/FloatingHeaderRow.java
new file mode 100644
index 0000000..922e4f1
--- /dev/null
+++ b/src/com/android/launcher3/allapps/FloatingHeaderRow.java
@@ -0,0 +1,58 @@
+/*
+ * 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.allapps;
+
+import android.graphics.Rect;
+import android.view.animation.Interpolator;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.anim.PropertySetter;
+
+/**
+ * A abstract representation of a row in all-apps view
+ */
+public interface FloatingHeaderRow {
+
+    FloatingHeaderRow[] NO_ROWS = new FloatingHeaderRow[0];
+
+    void setup(FloatingHeaderView parent, FloatingHeaderRow[] allRows, boolean tabsHidden);
+
+    void setInsets(Rect insets, DeviceProfile grid);
+
+    int getExpectedHeight();
+
+    /**
+     * Returns true if the row should draw based on its current position and layout.
+     */
+    boolean shouldDraw();
+
+    /**
+     * Returns true if the view has anything worth drawing. This is different than
+     * {@link #shouldDraw()} as this is called earlier in the layout to determine the view
+     * position.
+     */
+    boolean hasVisibleContent();
+
+    void setContentVisibility(boolean hasHeaderExtra, boolean hasContent,
+            PropertySetter setter, Interpolator fadeInterpolator);
+
+    /**
+     * Scrolls the content vertically.
+     */
+    void setVerticalScroll(int scroll, boolean isScrolledOut);
+
+    Class<? extends FloatingHeaderRow> getTypeClass();
+}
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index eaa7774..66dced9 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -19,9 +19,7 @@
 import android.content.Context;
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v7.widget.RecyclerView;
+import android.util.ArrayMap;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.View;
@@ -29,11 +27,26 @@
 import android.view.animation.Interpolator;
 import android.widget.LinearLayout;
 
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Insettable;
+import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
+import com.android.systemui.plugins.AllAppsRow;
+import com.android.systemui.plugins.AllAppsRow.OnHeightUpdatedListener;
+import com.android.systemui.plugins.PluginListener;
+
+import java.util.ArrayList;
+import java.util.Map;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.RecyclerView;
 
 public class FloatingHeaderView extends LinearLayout implements
-        ValueAnimator.AnimatorUpdateListener {
+        ValueAnimator.AnimatorUpdateListener, PluginListener<AllAppsRow>, Insettable,
+        OnHeightUpdatedListener {
 
     private final Rect mClip = new Rect(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
     private final ValueAnimator mAnimator = ValueAnimator.ofInt(0, 0);
@@ -55,10 +68,14 @@
 
             int current = -mCurrentRV.getCurrentScrollY();
             moved(current);
-            apply();
+            applyVerticalMove();
         }
     };
 
+    private final int mHeaderTopPadding;
+
+    protected final Map<AllAppsRow, PluginHeaderRow> mPluginRows = new ArrayMap<>();
+
     protected ViewGroup mTabLayout;
     private AllAppsRecyclerView mMainRV;
     private AllAppsRecyclerView mWorkRV;
@@ -75,21 +92,112 @@
     protected int mMaxTranslation;
     private boolean mMainRVActive = true;
 
+    private boolean mCollapsed = false;
+
+    // This is initialized once during inflation and stays constant after that. Fixed views
+    // cannot be added or removed dynamically.
+    private FloatingHeaderRow[] mFixedRows = FloatingHeaderRow.NO_ROWS;
+
+    // Array of all fixed rows and plugin rows. This is initialized every time a plugin is
+    // enabled or disabled, and represent the current set of all rows.
+    private FloatingHeaderRow[] mAllRows = FloatingHeaderRow.NO_ROWS;
+
     public FloatingHeaderView(@NonNull Context context) {
         this(context, null);
     }
 
     public FloatingHeaderView(@NonNull Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
+        mHeaderTopPadding = context.getResources()
+                .getDimensionPixelSize(R.dimen.all_apps_header_top_padding);
     }
 
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
         mTabLayout = findViewById(R.id.tabs);
+
+        // Find all floating header rows.
+        ArrayList<FloatingHeaderRow> rows = new ArrayList<>();
+        int count = getChildCount();
+        for (int i = 0; i < count; i++) {
+            View child = getChildAt(i);
+            if (child instanceof FloatingHeaderRow) {
+                rows.add((FloatingHeaderRow) child);
+            }
+        }
+        mFixedRows = rows.toArray(new FloatingHeaderRow[rows.size()]);
+        mAllRows = mFixedRows;
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        PluginManagerWrapper.INSTANCE.get(getContext()).addPluginListener(this,
+                AllAppsRow.class, true /* allowMultiple */);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        PluginManagerWrapper.INSTANCE.get(getContext()).removePluginListener(this);
+    }
+
+    private void recreateAllRowsArray() {
+        int pluginCount = mPluginRows.size();
+        if (pluginCount == 0) {
+            mAllRows = mFixedRows;
+        } else {
+            int count = mFixedRows.length;
+            mAllRows = new FloatingHeaderRow[count + pluginCount];
+            for (int i = 0; i < count; i++) {
+                mAllRows[i] = mFixedRows[i];
+            }
+
+            for (PluginHeaderRow row : mPluginRows.values()) {
+                mAllRows[count] = row;
+                count++;
+            }
+        }
+    }
+
+    @Override
+    public void onPluginConnected(AllAppsRow allAppsRowPlugin, Context context) {
+        PluginHeaderRow headerRow = new PluginHeaderRow(allAppsRowPlugin, this);
+        addView(headerRow.mView, indexOfChild(mTabLayout));
+        mPluginRows.put(allAppsRowPlugin, headerRow);
+        recreateAllRowsArray();
+        allAppsRowPlugin.setOnHeightUpdatedListener(this);
+    }
+
+    @Override
+    public void onHeightUpdated() {
+        int oldMaxHeight = mMaxTranslation;
+        updateExpectedHeight();
+
+        if (mMaxTranslation != oldMaxHeight) {
+            AllAppsContainerView parent = (AllAppsContainerView) getParent();
+            if (parent != null) {
+                parent.setupHeader();
+            }
+        }
+    }
+
+    @Override
+    public void onPluginDisconnected(AllAppsRow plugin) {
+        PluginHeaderRow row = mPluginRows.get(plugin);
+        removeView(row.mView);
+        mPluginRows.remove(plugin);
+        recreateAllRowsArray();
+        onHeightUpdated();
     }
 
     public void setup(AllAppsContainerView.AdapterHolder[] mAH, boolean tabsHidden) {
+        for (FloatingHeaderRow row : mAllRows) {
+            row.setup(this, mAllRows, tabsHidden);
+        }
+        updateExpectedHeight();
+
         mTabsHidden = tabsHidden;
         mTabLayout.setVisibility(tabsHidden ? View.GONE : View.VISIBLE);
         mMainRV = setupRV(mMainRV, mAH[AllAppsContainerView.AdapterHolder.MAIN].recyclerView);
@@ -106,6 +214,16 @@
         return updated;
     }
 
+    private void updateExpectedHeight() {
+        mMaxTranslation = 0;
+        if (mCollapsed) {
+            return;
+        }
+        for (FloatingHeaderRow row : mAllRows) {
+            mMaxTranslation += row.getExpectedHeight();
+        }
+    }
+
     public void setMainActive(boolean active) {
         mCurrentRV = active ? mMainRV : mWorkRV;
         mMainRVActive = active;
@@ -149,12 +267,21 @@
         }
     }
 
-    protected void applyScroll(int uncappedY, int currentY) { }
-
-    protected void apply() {
+    protected void applyVerticalMove() {
         int uncappedTranslationY = mTranslationY;
         mTranslationY = Math.max(mTranslationY, -mMaxTranslation);
-        applyScroll(uncappedTranslationY, mTranslationY);
+
+        if (mCollapsed || uncappedTranslationY < mTranslationY - mHeaderTopPadding) {
+            // we hide it completely if already capped (for opening search anim)
+            for (FloatingHeaderRow row : mAllRows) {
+                row.setVerticalScroll(0, true /* isScrolledOut */);
+            }
+        } else {
+            for (FloatingHeaderRow row : mAllRows) {
+                row.setVerticalScroll(uncappedTranslationY, false /* isScrolledOut */);
+            }
+        }
+
         mTabLayout.setTranslationY(mTranslationY);
         mClip.top = mMaxTranslation + mTranslationY;
         // clipping on a draw might cause additional redraw
@@ -164,6 +291,16 @@
         }
     }
 
+    /**
+     * Hides all the floating rows
+     */
+    public void setCollapsed(boolean collapse) {
+        if (mCollapsed == collapse) return;
+
+        mCollapsed = collapse;
+        onHeightUpdated();
+    }
+
     public void reset(boolean animate) {
         if (mAnimator.isStarted()) {
             mAnimator.cancel();
@@ -175,7 +312,7 @@
             mAnimator.start();
         } else {
             mTranslationY = 0;
-            apply();
+            applyVerticalMove();
         }
         mHeaderCollapsed = false;
         mSnappedScrolledY = -mMaxTranslation;
@@ -189,7 +326,7 @@
     @Override
     public void onAnimationUpdate(ValueAnimator animation) {
         mTranslationY = (Integer) animation.getAnimatedValue();
-        apply();
+        applyVerticalMove();
     }
 
     @Override
@@ -228,8 +365,12 @@
 
     public void setContentVisibility(boolean hasHeader, boolean hasContent, PropertySetter setter,
             Interpolator fadeInterpolator) {
-        setter.setViewAlpha(this, hasContent ? 1 : 0, fadeInterpolator);
+        for (FloatingHeaderRow row : mAllRows) {
+            row.setContentVisibility(hasHeader, hasContent, setter, fadeInterpolator);
+        }
+
         allowTouchForwarding(hasContent);
+        setter.setFloat(mTabLayout, ALPHA, hasContent ? 1 : 0, fadeInterpolator);
     }
 
     protected void allowTouchForwarding(boolean allow) {
@@ -237,6 +378,11 @@
     }
 
     public boolean hasVisibleContent() {
+        for (FloatingHeaderRow row : mAllRows) {
+            if (row.hasVisibleContent()) {
+                return true;
+            }
+        }
         return false;
     }
 
@@ -244,6 +390,23 @@
     public boolean hasOverlappingRendering() {
         return false;
     }
+
+    @Override
+    public void setInsets(Rect insets) {
+        DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
+        for (FloatingHeaderRow row : mAllRows) {
+            row.setInsets(insets, grid);
+        }
+    }
+
+    public <T extends FloatingHeaderRow> T findFixedRowByType(Class<T> type) {
+        for (FloatingHeaderRow row : mAllRows) {
+            if (row.getTypeClass() == type) {
+                return (T) row;
+            }
+        }
+        return null;
+    }
 }
 
 
diff --git a/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java b/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java
index a916697..decdcc0 100644
--- a/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java
+++ b/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java
@@ -19,8 +19,6 @@
 import android.content.SharedPreferences;
 import android.graphics.Canvas;
 import android.graphics.Paint;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.Button;
@@ -32,6 +30,9 @@
 import com.android.launcher3.pageindicators.PageIndicator;
 import com.android.launcher3.util.Themes;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 /**
  * Supports two indicator colors, dedicated for personal and work tabs.
  */
@@ -39,7 +40,7 @@
     private static final int POSITION_PERSONAL = 0;
     private static final int POSITION_WORK = 1;
 
-    private static final String KEY_SHOWED_PEEK_WORK_TAB = "showed_peek_work_tab";
+    public static final String KEY_SHOWED_PEEK_WORK_TAB = "showed_peek_work_tab";
 
     private final Paint mSelectedIndicatorPaint;
     private final Paint mDividerPaint;
diff --git a/src/com/android/launcher3/allapps/PluginHeaderRow.java b/src/com/android/launcher3/allapps/PluginHeaderRow.java
new file mode 100644
index 0000000..b283ff4
--- /dev/null
+++ b/src/com/android/launcher3/allapps/PluginHeaderRow.java
@@ -0,0 +1,85 @@
+/*
+ * 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.allapps;
+
+import static android.view.View.ALPHA;
+import static android.view.View.INVISIBLE;
+import static android.view.View.VISIBLE;
+
+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;
+
+/**
+ * Wrapper over an {@link AllAppsRow} plugin with {@link FloatingHeaderRow} interface so that
+ * it can be easily added in {@link FloatingHeaderView}.
+ */
+public class PluginHeaderRow implements FloatingHeaderRow {
+
+    private final AllAppsRow mPlugin;
+    final View mView;
+
+    PluginHeaderRow(AllAppsRow plugin, FloatingHeaderView parent) {
+        mPlugin = plugin;
+        mView = mPlugin.setup(parent);
+    }
+
+    @Override
+    public void setup(FloatingHeaderView parent, FloatingHeaderRow[] allRows,
+            boolean tabsHidden) { }
+
+    @Override
+    public void setInsets(Rect insets, DeviceProfile grid) { }
+
+    @Override
+    public int getExpectedHeight() {
+        return mPlugin.getExpectedHeight();
+    }
+
+    @Override
+    public boolean shouldDraw() {
+        return true;
+    }
+
+    @Override
+    public boolean hasVisibleContent() {
+        return true;
+    }
+
+    @Override
+    public void setContentVisibility(boolean hasHeaderExtra, boolean hasContent,
+            PropertySetter setter, Interpolator fadeInterpolator) {
+        // Don't use setViewAlpha as we want to control the visibility ourselves.
+        setter.setFloat(mView, ALPHA, hasContent ? 1 : 0, fadeInterpolator);
+    }
+
+    @Override
+    public void setVerticalScroll(int scroll, boolean isScrolledOut) {
+        mView.setVisibility(isScrolledOut ? INVISIBLE : VISIBLE);
+        if (!isScrolledOut) {
+            mView.setTranslationY(scroll);
+        }
+    }
+
+    @Override
+    public Class<PluginHeaderRow> getTypeClass() {
+        return PluginHeaderRow.class;
+    }
+}
\ 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 68193f5..cf9a088 100644
--- a/src/com/android/launcher3/allapps/SearchUiManager.java
+++ b/src/com/android/launcher3/allapps/SearchUiManager.java
@@ -15,7 +15,11 @@
  */
 package com.android.launcher3.allapps;
 
+import android.graphics.Rect;
 import android.view.KeyEvent;
+import android.view.animation.Interpolator;
+
+import com.android.launcher3.anim.PropertySetter;
 
 /**
  * Interface for controlling the Apps search UI.
@@ -37,4 +41,15 @@
      * some UI beforehand.
      */
     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);
 }
diff --git a/src/com/android/launcher3/allapps/WorkModeSwitch.java b/src/com/android/launcher3/allapps/WorkModeSwitch.java
index e7cf092..717bbd4 100644
--- a/src/com/android/launcher3/allapps/WorkModeSwitch.java
+++ b/src/com/android/launcher3/allapps/WorkModeSwitch.java
@@ -20,10 +20,12 @@
 import android.os.Process;
 import android.os.UserHandle;
 import android.util.AttributeSet;
+import android.view.MotionEvent;
 import android.widget.Switch;
 
 import com.android.launcher3.compat.UserManagerCompat;
 
+import java.lang.ref.WeakReference;
 import java.util.List;
 
 public class WorkModeSwitch extends Switch {
@@ -60,35 +62,63 @@
         setEnabled(true);
     }
 
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        return ev.getActionMasked() == MotionEvent.ACTION_MOVE || super.onTouchEvent(ev);
+    }
+
     private void trySetQuietModeEnabledToAllProfilesAsync(boolean enabled) {
-        new AsyncTask<Void, Void, Boolean>() {
+        new SetQuietModeEnabledAsyncTask(enabled, new WeakReference<>(this)).execute();
+    }
 
-            @Override
-            protected void onPreExecute() {
-                super.onPreExecute();
-                setEnabled(false);
+    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) {
-                UserManagerCompat userManager = UserManagerCompat.getInstance(getContext());
-                List<UserHandle> userProfiles = userManager.getUserProfiles();
-                boolean showConfirm = false;
-                for (UserHandle userProfile : userProfiles) {
-                    if (Process.myUserHandle().equals(userProfile)) {
-                        continue;
-                    }
-                    showConfirm |= !userManager.requestQuietModeEnabled(enabled, userProfile);
+        @Override
+        protected Boolean doInBackground(Void... voids) {
+            WorkModeSwitch workModeSwitch = switchWeakReference.get();
+            if (workModeSwitch == null) {
+                return false;
+            }
+            UserManagerCompat userManager =
+                    UserManagerCompat.getInstance(workModeSwitch.getContext());
+            List<UserHandle> userProfiles = userManager.getUserProfiles();
+            boolean showConfirm = false;
+            for (UserHandle userProfile : userProfiles) {
+                if (Process.myUserHandle().equals(userProfile)) {
+                    continue;
                 }
-                return showConfirm;
+                showConfirm |= !userManager.requestQuietModeEnabled(enabled, userProfile);
             }
+            return showConfirm;
+        }
 
-            @Override
-            protected void onPostExecute(Boolean showConfirm) {
-                if (showConfirm) {
-                    setEnabled(true);
+        @Override
+        protected void onPostExecute(Boolean showConfirm) {
+            if (showConfirm) {
+                WorkModeSwitch workModeSwitch = switchWeakReference.get();
+                if (workModeSwitch != null) {
+                    workModeSwitch.setEnabled(true);
                 }
             }
-        }.execute();
+        }
     }
 }
diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
index dcc4554..4515dde 100644
--- a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
@@ -15,8 +15,6 @@
  */
 package com.android.launcher3.allapps.search;
 
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
 import android.text.Editable;
 import android.text.TextUtils;
 import android.text.TextWatcher;
@@ -30,6 +28,7 @@
 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;
 
@@ -113,7 +112,8 @@
             return false;
         }
         return mLauncher.startActivitySafely(v,
-                PackageManagerHelper.getMarketSearchIntent(mLauncher, query), null);
+                PackageManagerHelper.getMarketSearchIntent(mLauncher, query), null,
+                AppLaunchTracker.CONTAINER_SEARCH);
     }
 
     @Override
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index ab6635e..31fcc8c 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -19,7 +19,9 @@
 import static android.view.View.MeasureSpec.getSize;
 import static android.view.View.MeasureSpec.makeMeasureSpec;
 
-import static com.android.launcher3.graphics.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
+import static com.android.launcher3.LauncherState.ALL_APPS_HEADER;
+import static com.android.launcher3.Utilities.prefixTextWithIcon;
+import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
 
 import android.content.Context;
 import android.graphics.Rect;
@@ -32,16 +34,19 @@
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewGroup.MarginLayoutParams;
+import android.view.animation.Interpolator;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.ExtendedEditText;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.allapps.AllAppsContainerView;
 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.graphics.TintedDrawableSpan;
 import com.android.launcher3.util.ComponentKey;
 
@@ -86,14 +91,7 @@
         mFixedTranslationY = getTranslationY();
         mMarginTopAdjusting = mFixedTranslationY - getPaddingTop();
 
-        // Update the hint to contain the icon.
-        // Prefix the original hint with two spaces. The first space gets replaced by the icon
-        // using span. The second space is used for a singe space character between the hint
-        // and the icon.
-        SpannableString spanned = new SpannableString("  " + getHint());
-        spanned.setSpan(new TintedDrawableSpan(getContext(), R.drawable.ic_allapps_search),
-                0, 1, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
-        setHint(spanned);
+        setHint(prefixTextWithIcon(getContext(), R.drawable.ic_allapps_search, getHint()));
     }
 
     @Override
@@ -205,13 +203,22 @@
         MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams();
         mlp.topMargin = Math.round(Math.max(-mFixedTranslationY, insets.top - mMarginTopAdjusting));
         requestLayout();
+    }
 
-        DeviceProfile dp = mLauncher.getDeviceProfile();
-        if (dp.isVerticalBarLayout()) {
-            mLauncher.getAllAppsController().setScrollRangeDelta(0);
+    @Override
+    public float getScrollRangeDelta(Rect insets) {
+        if (mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+            return 0;
         } else {
-            mLauncher.getAllAppsController().setScrollRangeDelta(
-                    insets.bottom + mlp.topMargin + mFixedTranslationY);
+            int topMargin = Math.round(Math.max(
+                    -mFixedTranslationY, insets.top - mMarginTopAdjusting));
+           return insets.bottom + topMargin + mFixedTranslationY;
         }
     }
+
+    @Override
+    public void setContentVisibility(int visibleElements, PropertySetter setter,
+            Interpolator interpolator) {
+        setter.setViewAlpha(this, (visibleElements & ALL_APPS_HEADER) != 0 ? 1 : 0, interpolator);
+    }
 }
diff --git a/src/com/android/launcher3/anim/AlphaUpdateListener.java b/src/com/android/launcher3/anim/AlphaUpdateListener.java
index a3d02d9..8ac9d66 100644
--- a/src/com/android/launcher3/anim/AlphaUpdateListener.java
+++ b/src/com/android/launcher3/anim/AlphaUpdateListener.java
@@ -20,6 +20,7 @@
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.view.View;
+import android.view.ViewGroup;
 
 /**
  * A convenience class to update a view's visibility state after an alpha animation.
@@ -55,7 +56,15 @@
             view.setVisibility(View.INVISIBLE);
         } else if (view.getAlpha() > ALPHA_CUTOFF_THRESHOLD
                 && view.getVisibility() != View.VISIBLE) {
-            view.setVisibility(View.VISIBLE);
+            if (view instanceof ViewGroup) {
+                ViewGroup viewGroup = ((ViewGroup) view);
+                int oldFocusability = viewGroup.getDescendantFocusability();
+                viewGroup.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+                viewGroup.setVisibility(View.VISIBLE);
+                viewGroup.setDescendantFocusability(oldFocusability);
+            } else {
+                view.setVisibility(View.VISIBLE);
+            }
         }
     }
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/anim/AnimatorPlaybackController.java b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
index 164728a..2c440bb 100644
--- a/src/com/android/launcher3/anim/AnimatorPlaybackController.java
+++ b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
@@ -16,6 +16,7 @@
 package com.android.launcher3.anim;
 
 import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
 
 import android.animation.Animator;
 import android.animation.Animator.AnimatorListener;
@@ -23,10 +24,16 @@
 import android.animation.AnimatorSet;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
+import android.util.Log;
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
+
+import androidx.dynamicanimation.animation.DynamicAnimation;
+import androidx.dynamicanimation.animation.SpringAnimation;
 
 /**
  * Helper class to control the playback of an {@link AnimatorSet}, with custom interpolators
@@ -37,6 +44,9 @@
  */
 public abstract class AnimatorPlaybackController implements ValueAnimator.AnimatorUpdateListener {
 
+    private static final String TAG = "AnimatorPlaybackCtrler";
+    private static boolean DEBUG = false;
+
     public static AnimatorPlaybackController wrap(AnimatorSet anim, long duration) {
         return wrap(anim, duration, null);
     }
@@ -60,6 +70,7 @@
     private final long mDuration;
 
     protected final AnimatorSet mAnim;
+    private Set<SpringAnimation> mSprings;
 
     protected float mCurrentFraction;
     private Runnable mEndAction;
@@ -67,6 +78,12 @@
     protected boolean mTargetCancelled = false;
     protected Runnable mOnCancelRunnable;
 
+    private OnAnimationEndDispatcher mEndListener;
+    private DynamicAnimation.OnAnimationEndListener mSpringEndListener;
+    // We need this variable to ensure the end listener is called immediately, otherwise we run into
+    // issues where the callback interferes with the states of the swipe detector.
+    private boolean mSkipToEnd = false;
+
     protected AnimatorPlaybackController(AnimatorSet anim, long duration,
             Runnable onCancelRunnable) {
         mAnim = anim;
@@ -75,7 +92,8 @@
 
         mAnimationPlayer = ValueAnimator.ofFloat(0, 1);
         mAnimationPlayer.setInterpolator(LINEAR);
-        mAnimationPlayer.addListener(new OnAnimationEndDispatcher());
+        mEndListener = new OnAnimationEndDispatcher();
+        mAnimationPlayer.addListener(mEndListener);
         mAnimationPlayer.addUpdateListener(this);
 
         mAnim.addListener(new AnimatorListenerAdapter() {
@@ -99,6 +117,15 @@
                 mTargetCancelled = false;
             }
         });
+
+        mSprings = new HashSet<>();
+        mSpringEndListener = (animation, canceled, value, velocity1) -> {
+            if (canceled) {
+                mEndListener.onAnimationCancel(mAnimationPlayer);
+            } else {
+                mEndListener.onAnimationEnd(mAnimationPlayer);
+            }
+        };
     }
 
     public AnimatorSet getTarget() {
@@ -154,6 +181,10 @@
         return mCurrentFraction;
     }
 
+    public float getInterpolatedProgress() {
+        return getInterpolator().getInterpolation(mCurrentFraction);
+    }
+
     /**
      * Sets the action to be called when the animation is completed. Also clears any
      * previously set action.
@@ -176,12 +207,39 @@
         }
     }
 
+    /**
+     * Starts playback and sets the spring.
+     */
+    public void dispatchOnStartWithVelocity(float end, float velocity) {
+        if (!QUICKSTEP_SPRINGS.get()) {
+            dispatchOnStart();
+            return;
+        }
+
+        if (DEBUG) Log.d(TAG, "dispatchOnStartWithVelocity#end=" + end + ", velocity=" + velocity);
+
+        for (Animator a : mAnim.getChildAnimations()) {
+            if (a instanceof SpringObjectAnimator) {
+                if (DEBUG) Log.d(TAG, "Found springAnimator=" + a);
+                SpringObjectAnimator springAnimator = (SpringObjectAnimator) a;
+                mSprings.add(springAnimator.getSpring());
+                springAnimator.startSpring(end, velocity, mSpringEndListener);
+            }
+        }
+
+        dispatchOnStart();
+    }
+
     public void dispatchOnStart() {
         dispatchOnStartRecursively(mAnim);
     }
 
     private void dispatchOnStartRecursively(Animator animator) {
-        for (AnimatorListener l : nonNullList(animator.getListeners())) {
+        List<AnimatorListener> listeners = animator instanceof SpringObjectAnimator
+                ? nonNullList(((SpringObjectAnimator) animator).getObjectAnimatorListeners())
+                : nonNullList(animator.getListeners());
+
+        for (AnimatorListener l : listeners) {
             l.onAnimationStart(animator);
         }
 
@@ -229,6 +287,17 @@
         return mOnCancelRunnable;
     }
 
+    public void skipToEnd() {
+        mSkipToEnd = true;
+        for (SpringAnimation spring : mSprings) {
+            if (spring.canSkipToEnd()) {
+                spring.skipToEnd();
+            }
+        }
+        mAnimationPlayer.end();
+        mSkipToEnd = false;
+    }
+
     public static class AnimatorPlaybackControllerVL extends AnimatorPlaybackController {
 
         private final ValueAnimator[] mChildAnimations;
@@ -278,18 +347,48 @@
         }
     }
 
+    private boolean isAnySpringRunning() {
+        for (SpringAnimation spring : mSprings) {
+            if (spring.isRunning()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Only dispatches the on end actions once the animator and all springs have completed running.
+     */
     private class OnAnimationEndDispatcher extends AnimationSuccessListener {
 
+        boolean mAnimatorDone = false;
+        boolean mSpringsDone = false;
+        boolean mDispatched = false;
+
         @Override
         public void onAnimationStart(Animator animation) {
             mCancelled = false;
+            mDispatched = false;
         }
 
         @Override
         public void onAnimationSuccess(Animator animator) {
-            dispatchOnEndRecursively(mAnim);
-            if (mEndAction != null) {
-                mEndAction.run();
+            if (mSprings.isEmpty()) {
+                mSpringsDone = mAnimatorDone = true;
+            }
+            if (isAnySpringRunning()) {
+                mAnimatorDone = true;
+            } else {
+                mSpringsDone = true;
+            }
+
+            // We wait for the spring (if any) to finish running before completing the end callback.
+            if (!mDispatched && (mSkipToEnd || (mAnimatorDone && mSpringsDone))) {
+                dispatchOnEndRecursively(mAnim);
+                if (mEndAction != null) {
+                    mEndAction.run();
+                }
+                mDispatched = true;
             }
         }
 
diff --git a/src/com/android/launcher3/anim/AnimatorSetBuilder.java b/src/com/android/launcher3/anim/AnimatorSetBuilder.java
index 307f258..52a896e 100644
--- a/src/com/android/launcher3/anim/AnimatorSetBuilder.java
+++ b/src/com/android/launcher3/anim/AnimatorSetBuilder.java
@@ -20,8 +20,6 @@
 import android.util.SparseArray;
 import android.view.animation.Interpolator;
 
-import com.android.launcher3.LauncherAnimUtils;
-
 import java.util.ArrayList;
 import java.util.List;
 
@@ -32,20 +30,23 @@
 
     public static final int ANIM_VERTICAL_PROGRESS = 0;
     public static final int ANIM_WORKSPACE_SCALE = 1;
-    public static final int ANIM_WORKSPACE_FADE = 2;
-    public static final int ANIM_OVERVIEW_SCALE = 3;
-    public static final int ANIM_OVERVIEW_FADE = 4;
-    public static final int ANIM_ALL_APPS_FADE = 5;
+    public static final int ANIM_WORKSPACE_TRANSLATE = 2;
+    public static final int ANIM_WORKSPACE_FADE = 3;
+    public static final int ANIM_HOTSEAT_SCALE = 4;
+    public static final int ANIM_HOTSEAT_TRANSLATE = 5;
+    public static final int ANIM_OVERVIEW_SCALE = 6;
+    public static final int ANIM_OVERVIEW_TRANSLATE_X = 7;
+    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 FLAG_DONT_ANIMATE_OVERVIEW = 1 << 0;
 
     protected final ArrayList<Animator> mAnims = new ArrayList<>();
 
     private final SparseArray<Interpolator> mInterpolators = new SparseArray<>();
     private List<Runnable> mOnFinishRunnables = new ArrayList<>();
-
-    /**
-     * Associates a tag with all the animations added after this call.
-     */
-    public void startTag(Object obj) { }
+    private int mFlags = 0;
 
     public void play(Animator anim) {
         mAnims.add(anim);
@@ -56,7 +57,7 @@
     }
 
     public AnimatorSet build() {
-        AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
+        AnimatorSet anim = new AnimatorSet();
         anim.playTogether(mAnims);
         if (!mOnFinishRunnables.isEmpty()) {
             anim.addListener(new AnimationSuccessListener() {
@@ -79,4 +80,12 @@
     public void setInterpolator(int animId, Interpolator interpolator) {
         mInterpolators.put(animId, interpolator);
     }
+
+    public void addFlag(int flag) {
+        mFlags |= flag;
+    }
+
+    public boolean hasFlag(int flag) {
+        return (mFlags & flag) != 0;
+    }
 }
diff --git a/src/com/android/launcher3/anim/FlingSpringAnim.java b/src/com/android/launcher3/anim/FlingSpringAnim.java
new file mode 100644
index 0000000..eaf3b1c
--- /dev/null
+++ b/src/com/android/launcher3/anim/FlingSpringAnim.java
@@ -0,0 +1,87 @@
+/*
+ * 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.anim;
+
+import androidx.dynamicanimation.animation.DynamicAnimation.OnAnimationEndListener;
+import androidx.dynamicanimation.animation.FlingAnimation;
+import androidx.dynamicanimation.animation.FloatPropertyCompat;
+import androidx.dynamicanimation.animation.SpringAnimation;
+import androidx.dynamicanimation.animation.SpringForce;
+
+/**
+ * Given a property to animate and a target value and starting velocity, first apply friction to
+ * the fling until we pass the target, then apply a spring force to pull towards the target.
+ */
+public class FlingSpringAnim {
+
+    private static final float FLING_FRICTION = 1.5f;
+    private static final float SPRING_STIFFNESS = 200;
+    private static final float SPRING_DAMPING = 0.8f;
+
+    private final FlingAnimation mFlingAnim;
+    private SpringAnimation mSpringAnim;
+
+    private float mTargetPosition;
+
+    public <K> FlingSpringAnim(K object, FloatPropertyCompat<K> property, float startPosition,
+            float targetPosition, float startVelocity, float minVisChange, float minValue,
+            float maxValue, float springVelocityFactor, OnAnimationEndListener onEndListener) {
+        mFlingAnim = new FlingAnimation(object, property)
+                .setFriction(FLING_FRICTION)
+                // Have the spring pull towards the target if we've slowed down too much before
+                // reaching it.
+                .setMinimumVisibleChange(minVisChange)
+                .setStartVelocity(startVelocity)
+                .setMinValue(minValue)
+                .setMaxValue(maxValue);
+        mTargetPosition = targetPosition;
+
+        mFlingAnim.addEndListener(((animation, canceled, value, velocity) -> {
+            mSpringAnim = new SpringAnimation(object, property)
+                    .setStartValue(value)
+                    .setStartVelocity(velocity * springVelocityFactor)
+                    .setSpring(new SpringForce(mTargetPosition)
+                            .setStiffness(SPRING_STIFFNESS)
+                            .setDampingRatio(SPRING_DAMPING));
+            mSpringAnim.addEndListener(onEndListener);
+            mSpringAnim.animateToFinalPosition(mTargetPosition);
+        }));
+    }
+
+    public float getTargetPosition() {
+        return mTargetPosition;
+    }
+
+    public void updatePosition(float startPosition, float targetPosition) {
+        mFlingAnim.setMinValue(Math.min(startPosition, targetPosition))
+                .setMaxValue(Math.max(startPosition, targetPosition));
+        mTargetPosition = targetPosition;
+        if (mSpringAnim != null) {
+            mSpringAnim.animateToFinalPosition(mTargetPosition);
+        }
+    }
+
+    public void start() {
+        mFlingAnim.start();
+    }
+
+    public void end() {
+        mFlingAnim.cancel();
+        if (mSpringAnim.canSkipToEnd()) {
+            mSpringAnim.skipToEnd();
+        }
+    }
+}
diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java
index 675e26d..b169cb8 100644
--- a/src/com/android/launcher3/anim/Interpolators.java
+++ b/src/com/android/launcher3/anim/Interpolators.java
@@ -57,6 +57,8 @@
 
     public static final Interpolator EXAGGERATED_EASE;
 
+    public static final Interpolator INSTANT = t -> 1;
+
     private static final int MIN_SETTLE_DURATION = 200;
     private static final float OVERSHOOT_FACTOR = 0.9f;
 
@@ -69,6 +71,7 @@
     }
 
     public static final Interpolator OVERSHOOT_1_2 = new OvershootInterpolator(1.2f);
+    public static final Interpolator OVERSHOOT_1_7 = new OvershootInterpolator(1.7f);
 
     public static final Interpolator TOUCH_RESPONSE_INTERPOLATOR =
             new PathInterpolator(0.3f, 0f, 0.1f, 1f);
diff --git a/src/com/android/launcher3/anim/PropertyListBuilder.java b/src/com/android/launcher3/anim/PropertyListBuilder.java
index 33e7f66..acc3b45 100644
--- a/src/com/android/launcher3/anim/PropertyListBuilder.java
+++ b/src/com/android/launcher3/anim/PropertyListBuilder.java
@@ -1,5 +1,6 @@
 package com.android.launcher3.anim;
 
+import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
 import android.view.View;
 
@@ -44,7 +45,8 @@
         return this;
     }
 
-    public PropertyValuesHolder[] build() {
-        return mProperties.toArray(new PropertyValuesHolder[mProperties.size()]);
+    public ObjectAnimator build(View view) {
+        return ObjectAnimator.ofPropertyValuesHolder(view,
+                mProperties.toArray(new PropertyValuesHolder[mProperties.size()]));
     }
 }
diff --git a/src/com/android/launcher3/anim/RevealOutlineAnimation.java b/src/com/android/launcher3/anim/RevealOutlineAnimation.java
index afb8875..f99dabc 100644
--- a/src/com/android/launcher3/anim/RevealOutlineAnimation.java
+++ b/src/com/android/launcher3/anim/RevealOutlineAnimation.java
@@ -8,8 +8,6 @@
 import android.view.View;
 import android.view.ViewOutlineProvider;
 
-import com.android.launcher3.Utilities;
-
 /**
  * A {@link ViewOutlineProvider} that has helper functions to create reveal animations.
  * This class should be extended so that subclasses can define the reveal shape as the
@@ -58,16 +56,10 @@
 
         });
 
-        va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator arg0) {
-                float progress = (Float) arg0.getAnimatedValue();
-                setProgress(progress);
-                revealView.invalidateOutline();
-                if (!Utilities.ATLEAST_LOLLIPOP_MR1) {
-                    revealView.invalidate();
-                }
-            }
+        va.addUpdateListener(v -> {
+            float progress = (Float) v.getAnimatedValue();
+            setProgress(progress);
+            revealView.invalidateOutline();
         });
         return va;
     }
diff --git a/src/com/android/launcher3/anim/SpringObjectAnimator.java b/src/com/android/launcher3/anim/SpringObjectAnimator.java
new file mode 100644
index 0000000..395fed2
--- /dev/null
+++ b/src/com/android/launcher3/anim/SpringObjectAnimator.java
@@ -0,0 +1,302 @@
+/*
+ * 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.anim;
+
+import static androidx.dynamicanimation.animation.FloatPropertyCompat.createFloatPropertyCompat;
+
+import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.FloatProperty;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+import androidx.dynamicanimation.animation.DynamicAnimation.OnAnimationEndListener;
+import androidx.dynamicanimation.animation.SpringAnimation;
+import androidx.dynamicanimation.animation.SpringForce;
+
+/**
+ * This animator allows for an object's property to be be controlled by an {@link ObjectAnimator} or
+ * a {@link SpringAnimation}. It extends ValueAnimator so it can be used in an AnimatorSet.
+ */
+public class SpringObjectAnimator<T> extends ValueAnimator {
+
+    private static final String TAG = "SpringObjectAnimator";
+    private static boolean DEBUG = false;
+
+    private ObjectAnimator mObjectAnimator;
+    private float[] mValues;
+
+    private SpringAnimation mSpring;
+    private SpringProperty<T> mProperty;
+
+    private ArrayList<AnimatorListener> mListeners;
+    private boolean mSpringEnded = true;
+    private boolean mAnimatorEnded = true;
+    private boolean mEnded = true;
+
+    public SpringObjectAnimator(T object, FloatProperty<T> property, float minimumVisibleChange,
+            float damping, float stiffness, float... values) {
+        mSpring = new SpringAnimation(object, createFloatPropertyCompat(property));
+        mSpring.setMinimumVisibleChange(minimumVisibleChange);
+        mSpring.setSpring(new SpringForce(0)
+                .setDampingRatio(damping)
+                .setStiffness(stiffness));
+        mSpring.setStartVelocity(0.01f);
+        mProperty = new SpringProperty<>(property, mSpring);
+        mObjectAnimator = ObjectAnimator.ofFloat(object, mProperty, values);
+        mValues = values;
+        mListeners = new ArrayList<>();
+        setFloatValues(values);
+
+        // We use this listener and track mListeners so that we can sync the animator and spring
+        // listeners.
+        mObjectAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mAnimatorEnded = false;
+                mEnded = false;
+                for (AnimatorListener l : mListeners) {
+                    l.onAnimationStart(animation);
+                }
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mAnimatorEnded = true;
+                tryEnding();
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                for (AnimatorListener l : mListeners) {
+                    l.onAnimationCancel(animation);
+                }
+                mSpring.cancel();
+            }
+        });
+
+        mSpring.addUpdateListener((animation, value, velocity) -> mSpringEnded = false);
+        mSpring.addEndListener((animation, canceled, value, velocity) -> {
+            mSpringEnded = true;
+            tryEnding();
+        });
+    }
+
+    private void tryEnding() {
+        if (DEBUG) {
+            Log.d(TAG, "tryEnding#mAnimatorEnded=" + mAnimatorEnded + ", mSpringEnded="
+                    + mSpringEnded + ", mEnded=" + mEnded);
+        }
+
+        // If springs are disabled, ignore value of mSpringEnded
+        if (mAnimatorEnded && (mSpringEnded || !QUICKSTEP_SPRINGS.get()) && !mEnded) {
+            for (AnimatorListener l : mListeners) {
+                l.onAnimationEnd(this);
+            }
+            mEnded = true;
+        }
+    }
+
+    public SpringAnimation getSpring() {
+        return mSpring;
+    }
+
+    /**
+     * Initializes and sets up the spring to take over controlling the object.
+     */
+    public void startSpring(float end, float velocity, OnAnimationEndListener endListener) {
+        // Cancel the spring so we can set new start velocity and final position. We need to remove
+        // the listener since the spring is not actually ending.
+        mSpring.removeEndListener(endListener);
+        mSpring.cancel();
+        mSpring.addEndListener(endListener);
+
+        mProperty.switchToSpring();
+
+        mSpring.setStartVelocity(velocity);
+
+        float startValue = end == 0 ? mValues[1] : mValues[0];
+        float endValue = end == 0 ? mValues[0] : mValues[1];
+        mSpring.setStartValue(startValue);
+        new Handler(Looper.getMainLooper()).postDelayed(() -> {
+            mSpring.animateToFinalPosition(endValue);
+        }, getStartDelay());
+    }
+
+    @Override
+    public void addListener(AnimatorListener listener) {
+        mListeners.add(listener);
+    }
+
+    public ArrayList<AnimatorListener> getObjectAnimatorListeners() {
+        return mObjectAnimator.getListeners();
+    }
+
+    @Override
+    public ArrayList<AnimatorListener> getListeners() {
+        return mListeners;
+    }
+
+    @Override
+    public void removeAllListeners() {
+        mListeners.clear();
+    }
+
+    @Override
+    public void removeListener(AnimatorListener listener) {
+        mListeners.remove(listener);
+    }
+
+    @Override
+    public void addPauseListener(AnimatorPauseListener listener) {
+        mObjectAnimator.addPauseListener(listener);
+    }
+
+    @Override
+    public void cancel() {
+        mObjectAnimator.cancel();
+        mSpring.cancel();
+    }
+
+    @Override
+    public void end() {
+        mObjectAnimator.end();
+    }
+
+    @Override
+    public long getDuration() {
+        return mObjectAnimator.getDuration();
+    }
+
+    @Override
+    public TimeInterpolator getInterpolator() {
+        return mObjectAnimator.getInterpolator();
+    }
+
+    @Override
+    public long getStartDelay() {
+        return mObjectAnimator.getStartDelay();
+    }
+
+    @Override
+    public long getTotalDuration() {
+        return mObjectAnimator.getTotalDuration();
+    }
+
+    @Override
+    public boolean isPaused() {
+        return mObjectAnimator.isPaused();
+    }
+
+    @Override
+    public boolean isRunning() {
+        return mObjectAnimator.isRunning();
+    }
+
+    @Override
+    public boolean isStarted() {
+        return mObjectAnimator.isStarted();
+    }
+
+    @Override
+    public void pause() {
+        mObjectAnimator.pause();
+    }
+
+    @Override
+    public void removePauseListener(AnimatorPauseListener listener) {
+        mObjectAnimator.removePauseListener(listener);
+    }
+
+    @Override
+    public void resume() {
+        mObjectAnimator.resume();
+    }
+
+    @Override
+    public ValueAnimator setDuration(long duration) {
+        return mObjectAnimator.setDuration(duration);
+    }
+
+    @Override
+    public void setInterpolator(TimeInterpolator value) {
+        mObjectAnimator.setInterpolator(value);
+    }
+
+    @Override
+    public void setStartDelay(long startDelay) {
+        mObjectAnimator.setStartDelay(startDelay);
+    }
+
+    @Override
+    public void setTarget(Object target) {
+        mObjectAnimator.setTarget(target);
+    }
+
+    @Override
+    public void start() {
+        mObjectAnimator.start();
+    }
+
+    @Override
+    public void setCurrentFraction(float fraction) {
+        mObjectAnimator.setCurrentFraction(fraction);
+    }
+
+    @Override
+    public void setCurrentPlayTime(long playTime) {
+        mObjectAnimator.setCurrentPlayTime(playTime);
+    }
+
+    public static class SpringProperty<T> extends FloatProperty<T> {
+
+        boolean useSpring = false;
+        final FloatProperty<T> mProperty;
+        final SpringAnimation mSpring;
+
+        public SpringProperty(FloatProperty<T> property, SpringAnimation spring) {
+            super(property.getName());
+            mProperty = property;
+            mSpring = spring;
+        }
+
+        public void switchToSpring() {
+            useSpring = true;
+        }
+
+        @Override
+        public Float get(T object) {
+            return mProperty.get(object);
+        }
+
+        @Override
+        public void setValue(T object, float progress) {
+            if (useSpring) {
+                mSpring.animateToFinalPosition(progress);
+            } else {
+                mProperty.setValue(object, progress);
+            }
+        }
+    }
+}
diff --git a/src/com/android/launcher3/badge/BadgeInfo.java b/src/com/android/launcher3/badge/BadgeInfo.java
deleted file mode 100644
index f03544f..0000000
--- a/src/com/android/launcher3/badge/BadgeInfo.java
+++ /dev/null
@@ -1,108 +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.badge;
-
-import com.android.launcher3.notification.NotificationInfo;
-import com.android.launcher3.notification.NotificationKeyData;
-import com.android.launcher3.util.PackageUserKey;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Contains data to be used in an icon badge.
- */
-public class BadgeInfo {
-
-    public static final int MAX_COUNT = 999;
-
-    /** Used to link this BadgeInfo to icons on the workspace and all apps */
-    private PackageUserKey mPackageUserKey;
-
-    /**
-     * The keys of the notifications that this badge represents. These keys can later be
-     * used to retrieve {@link NotificationInfo}'s.
-     */
-    private List<NotificationKeyData> mNotificationKeys;
-
-    /**
-     * The current sum of the counts in {@link #mNotificationKeys},
-     * updated whenever a key is added or removed.
-     */
-    private int mTotalCount;
-
-    public BadgeInfo(PackageUserKey packageUserKey) {
-        mPackageUserKey = packageUserKey;
-        mNotificationKeys = new ArrayList<>();
-    }
-
-    /**
-     * Returns whether the notification was added or its count changed.
-     */
-    public boolean addOrUpdateNotificationKey(NotificationKeyData notificationKey) {
-        int indexOfPrevKey = mNotificationKeys.indexOf(notificationKey);
-        NotificationKeyData prevKey = indexOfPrevKey == -1 ? null
-                : mNotificationKeys.get(indexOfPrevKey);
-        if (prevKey != null) {
-            if (prevKey.count == notificationKey.count) {
-                return false;
-            }
-            // Notification was updated with a new count.
-            mTotalCount -= prevKey.count;
-            mTotalCount += notificationKey.count;
-            prevKey.count = notificationKey.count;
-            return true;
-        }
-        boolean added = mNotificationKeys.add(notificationKey);
-        if (added) {
-            mTotalCount += notificationKey.count;
-        }
-        return added;
-    }
-
-    /**
-     * Returns whether the notification was removed (false if it didn't exist).
-     */
-    public boolean removeNotificationKey(NotificationKeyData notificationKey) {
-        boolean removed = mNotificationKeys.remove(notificationKey);
-        if (removed) {
-            mTotalCount -= notificationKey.count;
-        }
-        return removed;
-    }
-
-    public List<NotificationKeyData> getNotificationKeys() {
-        return mNotificationKeys;
-    }
-
-    public int getNotificationCount() {
-        return Math.min(mTotalCount, MAX_COUNT);
-    }
-
-    /**
-     * Whether newBadge represents the same PackageUserKey as this badge, and icons with
-     * this badge should be invalidated. So, for instance, if a badge has 3 notifications
-     * and one of those notifications is updated, this method should return false because
-     * the badge still says "3" and the contents of those notifications are only retrieved
-     * upon long-click. This method always returns true when adding or removing notifications,
-     * or if the badge has a notification icon to show.
-     */
-    public boolean shouldBeInvalidated(BadgeInfo newBadge) {
-        return mPackageUserKey.equals(newBadge.mPackageUserKey)
-                && (getNotificationCount() != newBadge.getNotificationCount());
-    }
-}
diff --git a/src/com/android/launcher3/badge/BadgeRenderer.java b/src/com/android/launcher3/badge/BadgeRenderer.java
deleted file mode 100644
index 9487427..0000000
--- a/src/com/android/launcher3/badge/BadgeRenderer.java
+++ /dev/null
@@ -1,99 +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.badge;
-
-import static android.graphics.Paint.ANTI_ALIAS_FLAG;
-import static android.graphics.Paint.FILTER_BITMAP_FLAG;
-
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.util.Log;
-
-import com.android.launcher3.graphics.ShadowGenerator;
-
-/**
- * Contains parameters necessary to draw a badge for an icon (e.g. the size of the badge).
- * @see BadgeInfo for the data to draw
- */
-public class BadgeRenderer {
-
-    private static final String TAG = "BadgeRenderer";
-
-    // The badge sizes are defined as percentages of the app icon size.
-    private static final float SIZE_PERCENTAGE = 0.38f;
-
-    // Extra scale down of the dot
-    private static final float DOT_SCALE = 0.6f;
-
-    // Used to expand the width of the badge for each additional digit.
-    private static final float OFFSET_PERCENTAGE = 0.02f;
-
-    private final float mDotCenterOffset;
-    private final int mOffset;
-    private final float mCircleRadius;
-    private final Paint mCirclePaint = new Paint(ANTI_ALIAS_FLAG | FILTER_BITMAP_FLAG);
-
-    private final Bitmap mBackgroundWithShadow;
-    private final float mBitmapOffset;
-
-    public BadgeRenderer(int iconSizePx) {
-        mDotCenterOffset = SIZE_PERCENTAGE * iconSizePx;
-        mOffset = (int) (OFFSET_PERCENTAGE * iconSizePx);
-
-        int size = (int) (DOT_SCALE * mDotCenterOffset);
-        ShadowGenerator.Builder builder = new ShadowGenerator.Builder(Color.TRANSPARENT);
-        mBackgroundWithShadow = builder.setupBlurForSize(size).createPill(size, size);
-        mCircleRadius = builder.radius;
-
-        mBitmapOffset = -mBackgroundWithShadow.getHeight() * 0.5f; // Same as width.
-    }
-
-    /**
-     * Draw a circle in the top right corner of the given bounds, and draw
-     * {@link BadgeInfo#getNotificationCount()} on top of the circle.
-     * @param color The color (based on the icon) to use for the badge.
-     * @param iconBounds The bounds of the icon being badged.
-     * @param badgeScale The progress of the animation, from 0 to 1.
-     * @param spaceForOffset How much space is available to offset the badge up and to the right.
-     */
-    public void draw(
-            Canvas canvas, int color, Rect iconBounds, float badgeScale, Point spaceForOffset) {
-        if (iconBounds == null || spaceForOffset == null) {
-            Log.e(TAG, "Invalid null argument(s) passed in call to draw.");
-            return;
-        }
-        canvas.save();
-        // We draw the badge relative to its center.
-        float badgeCenterX = iconBounds.right - mDotCenterOffset / 2;
-        float badgeCenterY = iconBounds.top + mDotCenterOffset / 2;
-
-        int offsetX = Math.min(mOffset, spaceForOffset.x);
-        int offsetY = Math.min(mOffset, spaceForOffset.y);
-        canvas.translate(badgeCenterX + offsetX, badgeCenterY - offsetY);
-        canvas.scale(badgeScale, badgeScale);
-
-        mCirclePaint.setColor(Color.BLACK);
-        canvas.drawBitmap(mBackgroundWithShadow, mBitmapOffset, mBitmapOffset, mCirclePaint);
-        mCirclePaint.setColor(color);
-        canvas.drawCircle(0, 0, mCircleRadius, mCirclePaint);
-        canvas.restore();
-    }
-}
diff --git a/src/com/android/launcher3/badge/FolderBadgeInfo.java b/src/com/android/launcher3/badge/FolderBadgeInfo.java
deleted file mode 100644
index 3a1bf60..0000000
--- a/src/com/android/launcher3/badge/FolderBadgeInfo.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.badge;
-
-import com.android.launcher3.Utilities;
-
-/**
- * Subclass of BadgeInfo that only contains the badge count, which is
- * the sum of all the Folder's items' notifications (each counts as 1).
- */
-public class FolderBadgeInfo extends BadgeInfo {
-
-    private static final int MIN_COUNT = 0;
-
-    private int mNumNotifications;
-
-    public FolderBadgeInfo() {
-        super(null);
-    }
-
-    public void addBadgeInfo(BadgeInfo badgeToAdd) {
-        if (badgeToAdd == null) {
-            return;
-        }
-        mNumNotifications += badgeToAdd.getNotificationKeys().size();
-        mNumNotifications = Utilities.boundToRange(
-                mNumNotifications, MIN_COUNT, BadgeInfo.MAX_COUNT);
-    }
-
-    public void subtractBadgeInfo(BadgeInfo badgeToSubtract) {
-        if (badgeToSubtract == null) {
-            return;
-        }
-        mNumNotifications -= badgeToSubtract.getNotificationKeys().size();
-        mNumNotifications = Utilities.boundToRange(
-                mNumNotifications, MIN_COUNT, BadgeInfo.MAX_COUNT);
-    }
-
-    @Override
-    public int getNotificationCount() {
-        // This forces the folder badge to always show up as a dot.
-        return 0;
-    }
-
-    public boolean hasBadge() {
-        return mNumNotifications > 0;
-    }
-}
diff --git a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
index 0c78381..43ae651 100644
--- a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
+++ b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
@@ -17,9 +17,16 @@
 package com.android.launcher3.compat;
 
 import android.content.Context;
+import android.os.Bundle;
 import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.Utilities;
+
+import java.util.function.Consumer;
 
 public class AccessibilityManagerCompat {
 
@@ -44,4 +51,83 @@
     private static AccessibilityManager getManager(Context context) {
         return (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
     }
+
+    public static void sendStateEventToTest(Context context, int stateOrdinal) {
+        if (com.android.launcher3.testing.TestProtocol.sDebugTracing) {
+            android.util.Log.e(TestProtocol.NO_ALLAPPS_EVENT_TAG, "sendStateEventToTest");
+        }
+        final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context);
+        if (accessibilityManager == null) return;
+
+        final Bundle parcel = new Bundle();
+        parcel.putInt(TestProtocol.STATE_FIELD, stateOrdinal);
+
+        sendEventToTest(accessibilityManager, TestProtocol.SWITCHED_TO_STATE_MESSAGE, parcel);
+    }
+
+    public static void sendScrollFinishedEventToTest(Context context) {
+        final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context);
+        if (accessibilityManager == null) return;
+
+        sendEventToTest(accessibilityManager, 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);
+    }
+
+    private static void sendEventToTest(
+            AccessibilityManager accessibilityManager, String eventTag, Bundle data) {
+        final AccessibilityEvent e = AccessibilityEvent.obtain(
+                AccessibilityEvent.TYPE_ANNOUNCEMENT);
+        e.setClassName(eventTag);
+        e.setParcelableData(data);
+        accessibilityManager.sendAccessibilityEvent(e);
+    }
+
+    /**
+     * Returns accessibility manager to be used for communication with UI Automation tests.
+     * The tests may exchange custom accessibility messages with the launcher; the accessibility
+     * manager is used in these communications.
+     *
+     * If the launcher runs not under a test, the return is null, and no attempt to process or send
+     * custom accessibility messages should be made.
+     */
+    private static AccessibilityManager getAccessibilityManagerForTest(Context context) {
+        // If not running in a test harness, don't participate in test exchanges.
+        if (!Utilities.IS_RUNNING_IN_TEST_HARNESS) return null;
+
+        final AccessibilityManager accessibilityManager = getManager(context);
+        if (!accessibilityManager.isEnabled()) return null;
+
+        return accessibilityManager;
+    }
+
+    public static boolean processTestRequest(Context context, String eventTag, int action,
+            Bundle request, Consumer<Bundle> responseFiller) {
+        final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context);
+        if (accessibilityManager == null) return false;
+
+        // The test sends a request via a ACTION_SET_TEXT.
+        if (action == AccessibilityNodeInfo.ACTION_SET_TEXT &&
+                eventTag.equals(request.getCharSequence(
+                        AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE))) {
+            final Bundle response = new Bundle();
+            responseFiller.accept(response);
+            AccessibilityManagerCompat.sendEventToTest(
+                    accessibilityManager, eventTag + TestProtocol.RESPONSE_MESSAGE_POSTFIX, response);
+            return true;
+        }
+        return false;
+    }
+
+    public static int getRecommendedTimeoutMillis(Context context, int originalTimeout, int flags) {
+        if (Utilities.ATLEAST_Q) {
+            return getManager(context).getRecommendedTimeoutMillis(originalTimeout, flags);
+        }
+        return originalTimeout;
+    }
 }
diff --git a/src/com/android/launcher3/compat/AlphabeticIndexCompat.java b/src/com/android/launcher3/compat/AlphabeticIndexCompat.java
index 84e82e3..dfdcc70 100644
--- a/src/com/android/launcher3/compat/AlphabeticIndexCompat.java
+++ b/src/com/android/launcher3/compat/AlphabeticIndexCompat.java
@@ -1,17 +1,16 @@
 package com.android.launcher3.compat;
 
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.icu.text.AlphabeticIndex;
-import android.os.Build;
 import android.os.LocaleList;
 import android.util.Log;
 
 import com.android.launcher3.Utilities;
 
-import java.lang.reflect.Method;
 import java.util.Locale;
 
+import androidx.annotation.NonNull;
+
 public class AlphabeticIndexCompat {
     private static final String TAG = "AlphabeticIndexCompat";
 
@@ -23,19 +22,10 @@
         BaseIndex index = null;
 
         try {
-            if (Utilities.ATLEAST_NOUGAT) {
-                index = new AlphabeticIndexVN(context);
-            }
+            index = new AlphabeticIndexVN(context);
         } catch (Exception e) {
             Log.d(TAG, "Unable to load the system index", e);
         }
-        if (index == null) {
-            try {
-                index = new AlphabeticIndexV16(context);
-            } catch (Exception e) {
-                Log.d(TAG, "Unable to load the system index", e);
-            }
-        }
 
         mBaseIndex = index == null ? new BaseIndex() : index;
 
@@ -53,7 +43,7 @@
     /**
      * Computes the section name for an given string {@param s}.
      */
-    public String computeSectionName(CharSequence cs) {
+    public String computeSectionName(@NonNull CharSequence cs) {
         String s = Utilities.trim(cs);
         String sectionName = mBaseIndex.getBucketLabel(mBaseIndex.getBucketIndex(s));
         if (Utilities.trim(sectionName).isEmpty() && s.length() > 0) {
@@ -89,7 +79,7 @@
         /**
          * Returns the index of the bucket in which the given string should appear.
          */
-        protected int getBucketIndex(String s) {
+        protected int getBucketIndex(@NonNull String s) {
             if (s.isEmpty()) {
                 return UNKNOWN_BUCKET_INDEX;
             }
@@ -109,59 +99,8 @@
     }
 
     /**
-     * Reflected libcore.icu.AlphabeticIndex implementation, falls back to the base
-     * alphabetic index.
-     */
-    private static class AlphabeticIndexV16 extends BaseIndex {
-
-        private Object mAlphabeticIndex;
-        private Method mGetBucketIndexMethod;
-        private Method mGetBucketLabelMethod;
-
-        public AlphabeticIndexV16(Context context) throws Exception {
-            Locale curLocale = context.getResources().getConfiguration().locale;
-            Class clazz = Class.forName("libcore.icu.AlphabeticIndex");
-            mGetBucketIndexMethod = clazz.getDeclaredMethod("getBucketIndex", String.class);
-            mGetBucketLabelMethod = clazz.getDeclaredMethod("getBucketLabel", int.class);
-            mAlphabeticIndex = clazz.getConstructor(Locale.class).newInstance(curLocale);
-
-            if (!curLocale.getLanguage().equals(Locale.ENGLISH.getLanguage())) {
-                clazz.getDeclaredMethod("addLabels", Locale.class)
-                        .invoke(mAlphabeticIndex, Locale.ENGLISH);
-            }
-        }
-
-        /**
-         * Returns the index of the bucket in which {@param s} should appear.
-         * Function is synchronized because underlying routine walks an iterator
-         * whose state is maintained inside the index object.
-         */
-        protected int getBucketIndex(String s) {
-            try {
-                return (Integer) mGetBucketIndexMethod.invoke(mAlphabeticIndex, s);
-            } catch (Exception e) {
-                e.printStackTrace();
-            }
-            return super.getBucketIndex(s);
-        }
-
-        /**
-         * Returns the label for the bucket at the given index (as returned by getBucketIndex).
-         */
-        protected String getBucketLabel(int index) {
-            try {
-                return (String) mGetBucketLabelMethod.invoke(mAlphabeticIndex, index);
-            } catch (Exception e) {
-                e.printStackTrace();
-            }
-            return super.getBucketLabel(index);
-        }
-    }
-
-    /**
      * Implementation based on {@link AlphabeticIndex}.
      */
-    @TargetApi(Build.VERSION_CODES.N)
     private static class AlphabeticIndexVN extends BaseIndex {
 
         private final AlphabeticIndex.ImmutableIndex mAlphabeticIndex;
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
index fd1f0cc..3243256 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
@@ -22,7 +22,6 @@
 import android.content.Context;
 import android.os.Bundle;
 import android.os.UserHandle;
-import android.support.annotation.Nullable;
 
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
@@ -35,6 +34,8 @@
 import java.util.HashMap;
 import java.util.List;
 
+import androidx.annotation.Nullable;
+
 public abstract class AppWidgetManagerCompat {
 
     private static final Object sInstanceLock = new Object();
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
index 8430285..1065748 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
@@ -23,7 +23,6 @@
 import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.support.annotation.Nullable;
 
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
@@ -38,6 +37,8 @@
 import java.util.Iterator;
 import java.util.List;
 
+import androidx.annotation.Nullable;
+
 class AppWidgetManagerCompatVL extends AppWidgetManagerCompat {
 
     private final UserManager mUserManager;
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatVO.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatVO.java
index 44158ed..b7b0563 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompatVO.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatVO.java
@@ -18,7 +18,6 @@
 
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
-import android.support.annotation.Nullable;
 
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.PackageUserKey;
@@ -26,6 +25,8 @@
 import java.util.Collections;
 import java.util.List;
 
+import androidx.annotation.Nullable;
+
 class AppWidgetManagerCompatVO extends AppWidgetManagerCompatVL {
 
     AppWidgetManagerCompatVO(Context context) {
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompat.java b/src/com/android/launcher3/compat/LauncherAppsCompat.java
index 2cac536..58fc73d 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompat.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompat.java
@@ -21,15 +21,19 @@
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.LauncherActivityInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.ShortcutInfo;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.UserHandle;
-import android.support.annotation.Nullable;
+
 import com.android.launcher3.Utilities;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.util.PackageUserKey;
+
 import java.util.List;
 
+import androidx.annotation.Nullable;
+
 public abstract class LauncherAppsCompat {
 
     public interface OnAppsChangedCallbackCompat {
@@ -40,7 +44,7 @@
         void onPackagesUnavailable(String[] packageNames, UserHandle user, boolean replacing);
         void onPackagesSuspended(String[] packageNames, UserHandle user);
         void onPackagesUnsuspended(String[] packageNames, UserHandle user);
-        void onShortcutsChanged(String packageName, List<ShortcutInfoCompat> shortcuts,
+        void onShortcutsChanged(String packageName, List<ShortcutInfo> shortcuts,
                 UserHandle user);
     }
 
@@ -53,7 +57,9 @@
     public static LauncherAppsCompat getInstance(Context context) {
         synchronized (sInstanceLock) {
             if (sInstance == null) {
-                if (Utilities.ATLEAST_OREO) {
+                if (Utilities.ATLEAST_Q) {
+                    sInstance = new LauncherAppsCompatVQ(context.getApplicationContext());
+                } else if (Utilities.ATLEAST_OREO) {
                     sInstance = new LauncherAppsCompatVO(context.getApplicationContext());
                 } else {
                     sInstance = new LauncherAppsCompatVL(context.getApplicationContext());
@@ -81,7 +87,5 @@
     public abstract List<ShortcutConfigActivityInfo> getCustomShortcutActivityList(
             @Nullable PackageUserKey packageUser);
 
-    public void showAppDetailsForProfile(ComponentName component, UserHandle user) {
-        showAppDetailsForProfile(component, user, null, null);
-    }
+    public abstract List<PackageInstaller.SessionInfo> getAllPackageInstallerSessions();
 }
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
index cc3e5a7..1d19b53 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
@@ -22,6 +22,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.LauncherApps;
+import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ShortcutInfo;
@@ -29,15 +30,17 @@
 import android.os.Bundle;
 import android.os.Process;
 import android.os.UserHandle;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
 import android.util.ArrayMap;
+
 import com.android.launcher3.compat.ShortcutConfigActivityInfo.ShortcutConfigActivityInfoVL;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.util.PackageUserKey;
+
 import java.util.ArrayList;
 import java.util.List;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 public class LauncherAppsCompatVL extends LauncherAppsCompat {
 
     protected final LauncherApps mLauncherApps;
@@ -176,12 +179,7 @@
         public void onShortcutsChanged(@NonNull String packageName,
             @NonNull List<ShortcutInfo> shortcuts,
             @NonNull UserHandle user) {
-            List<ShortcutInfoCompat> shortcutInfoCompats = new ArrayList<>(shortcuts.size());
-            for (ShortcutInfo shortcutInfo : shortcuts) {
-                shortcutInfoCompats.add(new ShortcutInfoCompat(shortcutInfo));
-            }
-
-            mCallback.onShortcutsChanged(packageName, shortcutInfoCompats, user);
+            mCallback.onShortcutsChanged(packageName, shortcuts, user);
         }
     }
 
@@ -197,10 +195,15 @@
                 pm.queryIntentActivities(new Intent(Intent.ACTION_CREATE_SHORTCUT), 0)) {
             if (packageUser == null || packageUser.mPackageName
                     .equals(info.activityInfo.packageName)) {
-                result.add(new ShortcutConfigActivityInfoVL(info.activityInfo, pm));
+                result.add(new ShortcutConfigActivityInfoVL(info.activityInfo));
             }
         }
         return result;
     }
+
+    @Override
+    public List<PackageInstaller.SessionInfo> getAllPackageInstallerSessions() {
+        return mContext.getPackageManager().getPackageInstaller().getAllSessions();
+    }
 }
 
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVO.java b/src/com/android/launcher3/compat/LauncherAppsCompatVO.java
index 173d0d1..6e7a1bd 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVO.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatVO.java
@@ -24,24 +24,25 @@
 import android.content.pm.LauncherApps;
 import android.content.pm.LauncherApps.PinItemRequest;
 import android.content.pm.PackageManager;
+import android.content.pm.ShortcutInfo;
 import android.os.Build;
 import android.os.Parcelable;
 import android.os.Process;
 import android.os.UserHandle;
-import android.support.annotation.Nullable;
 
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherModel;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.compat.ShortcutConfigActivityInfo.ShortcutConfigActivityInfoVO;
-import com.android.launcher3.graphics.LauncherIcons;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
+import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.util.LooperExecutor;
 import com.android.launcher3.util.PackageUserKey;
 
 import java.util.ArrayList;
 import java.util.List;
 
+import androidx.annotation.Nullable;
+
 @TargetApi(26)
 public class LauncherAppsCompatVO extends LauncherAppsCompatVL {
 
@@ -102,12 +103,12 @@
      * thread to UI thread.
      * If (d) happens before we add this shortcut to our model, we will end up unpinning
      * the shortcut in the system.
-     * Here its the caller's responsibility to add the newly created ShortcutInfo immediately
+     * Here its the caller's responsibility to add the newly created WorkspaceItemInfo immediately
      * to the model (which may involves a single post-to-worker-thread). That will guarantee
      * that (d) happens after model is updated.
      */
     @Nullable
-    public static ShortcutInfo createShortcutInfoFromPinItemRequest(
+    public static WorkspaceItemInfo createWorkspaceItemFromPinItemRequest(
             Context context, final PinItemRequest request, final long acceptDelay) {
         if (request != null &&
                 request.getRequestType() == PinItemRequest.REQUEST_TYPE_SHORTCUT &&
@@ -134,14 +135,14 @@
                 });
             }
 
-            ShortcutInfoCompat compat = new ShortcutInfoCompat(request.getShortcutInfo());
-            ShortcutInfo info = new ShortcutInfo(compat, context);
+            ShortcutInfo si = request.getShortcutInfo();
+            WorkspaceItemInfo info = new WorkspaceItemInfo(si, context);
             // Apply the unbadged icon and fetch the actual icon asynchronously.
             LauncherIcons li = LauncherIcons.obtain(context);
-            li.createShortcutIcon(compat, false /* badged */).applyTo(info);
+            info.applyFrom(li.createShortcutIcon(si, false /* badged */));
             li.recycle();
             LauncherAppState.getInstance(context).getModel()
-                    .updateAndBindShortcutInfo(info, compat);
+                    .updateAndBindWorkspaceItem(info, si);
             return info;
         } else {
             return null;
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVQ.java b/src/com/android/launcher3/compat/LauncherAppsCompatVQ.java
new file mode 100644
index 0000000..0a1811e
--- /dev/null
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatVQ.java
@@ -0,0 +1,36 @@
+/*
+ * 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.compat;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInstaller;
+
+import java.util.List;
+
+@TargetApi(29)
+public class LauncherAppsCompatVQ extends LauncherAppsCompatVO {
+
+    LauncherAppsCompatVQ(Context context) {
+        super(context);
+    }
+
+    public List<PackageInstaller.SessionInfo> getAllPackageInstallerSessions() {
+        return mLauncherApps.getAllPackageInstallerSessions();
+    }
+}
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompat.java b/src/com/android/launcher3/compat/PackageInstallerCompat.java
index 3270ba2..7dad7e9 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompat.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompat.java
@@ -19,11 +19,12 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageInstaller;
-import android.support.annotation.NonNull;
 
 import java.util.HashMap;
 import java.util.List;
 
+import androidx.annotation.NonNull;
+
 public abstract class PackageInstallerCompat {
 
     public static final int STATUS_INSTALLED = 0;
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
index dd17916..a34ca50 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
@@ -27,7 +27,8 @@
 import android.text.TextUtils;
 import android.util.SparseArray;
 
-import com.android.launcher3.IconCache;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.config.FeatureFlags;
@@ -49,6 +50,7 @@
     private final Handler mWorker;
     private final Context mAppContext;
     private final HashMap<String,Boolean> mSessionVerifiedMap = new HashMap<>();
+    private final LauncherAppsCompat mLauncherApps;
 
     PackageInstallerCompatVL(Context context) {
         mAppContext = context.getApplicationContext();
@@ -56,6 +58,7 @@
         mCache = LauncherAppState.getInstance(context).getIconCache();
         mWorker = new Handler(LauncherModel.getWorkerLooper());
         mInstaller.registerSessionCallback(mCallback, mWorker);
+        mLauncherApps = LauncherAppsCompat.getInstance(context);
     }
 
     @Override
@@ -171,7 +174,9 @@
 
     @Override
     public List<SessionInfo> getAllVerifiedSessions() {
-        List<SessionInfo> list = new ArrayList<>(mInstaller.getAllSessions());
+        List<SessionInfo> list = new ArrayList<>(Utilities.ATLEAST_Q
+                ? mLauncherApps.getAllPackageInstallerSessions()
+                : mInstaller.getAllSessions());
         Iterator<SessionInfo> it = list.iterator();
         while (it.hasNext()) {
             if (verify(it.next()) == null) {
diff --git a/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java b/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java
index 31c0087..ace5691 100644
--- a/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java
+++ b/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java
@@ -32,15 +32,16 @@
 import android.util.Log;
 import android.widget.Toast;
 
-import com.android.launcher3.IconCache;
+import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.icons.ComponentWithLabel;
+import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
-import com.android.launcher3.ShortcutInfo;
 
 /**
  * Wrapper class for representing a shortcut configure activity.
  */
-public abstract class ShortcutConfigActivityInfo {
+public abstract class ShortcutConfigActivityInfo implements ComponentWithLabel {
 
     private static final String TAG = "SCActivityInfo";
 
@@ -52,10 +53,12 @@
         mUser = user;
     }
 
+    @Override
     public ComponentName getComponent() {
         return mCn;
     }
 
+    @Override
     public UserHandle getUser() {
         return mUser;
     }
@@ -64,15 +67,13 @@
         return LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
     }
 
-    public abstract CharSequence getLabel();
-
     public abstract Drawable getFullResIcon(IconCache cache);
 
     /**
-     * Return a shortcut info, if it can be created directly on drop, without requiring any
+     * Return a WorkspaceItemInfo, if it can be created directly on drop, without requiring any
      * {@link #startConfigActivity(Activity, int)}.
      */
-    public ShortcutInfo createShortcutInfo() {
+    public WorkspaceItemInfo createWorkspaceItemInfo() {
         return null;
     }
 
@@ -94,8 +95,8 @@
     }
 
     /**
-     * Returns true if various properties ({@link #getLabel()}, {@link #getFullResIcon}) can
-     * be safely persisted.
+     * Returns true if various properties ({@link #getLabel(PackageManager)},
+     * {@link #getFullResIcon}) can be safely persisted.
      */
     public boolean isPersistable() {
         return true;
@@ -104,18 +105,15 @@
     static class ShortcutConfigActivityInfoVL extends ShortcutConfigActivityInfo {
 
         private final ActivityInfo mInfo;
-        private final PackageManager mPm;
 
-
-        public ShortcutConfigActivityInfoVL(ActivityInfo info, PackageManager pm) {
+        public ShortcutConfigActivityInfoVL(ActivityInfo info) {
             super(new ComponentName(info.packageName, info.name), Process.myUserHandle());
             mInfo = info;
-            mPm = pm;
         }
 
         @Override
-        public CharSequence getLabel() {
-            return mInfo.loadLabel(mPm);
+        public CharSequence getLabel(PackageManager pm) {
+            return mInfo.loadLabel(pm);
         }
 
         @Override
@@ -135,7 +133,7 @@
         }
 
         @Override
-        public CharSequence getLabel() {
+        public CharSequence getLabel(PackageManager pm) {
             return mInfo.getLabel();
         }
 
diff --git a/src/com/android/launcher3/compat/UserManagerCompat.java b/src/com/android/launcher3/compat/UserManagerCompat.java
index 03e3861..2c0088e 100644
--- a/src/com/android/launcher3/compat/UserManagerCompat.java
+++ b/src/com/android/launcher3/compat/UserManagerCompat.java
@@ -35,14 +35,8 @@
             if (sInstance == null) {
                 if (Utilities.ATLEAST_P) {
                     sInstance = new UserManagerCompatVP(context.getApplicationContext());
-                } else if (Utilities.ATLEAST_NOUGAT_MR1) {
-                    sInstance = new UserManagerCompatVNMr1(context.getApplicationContext());
-                } else if (Utilities.ATLEAST_NOUGAT) {
-                    sInstance = new UserManagerCompatVN(context.getApplicationContext());
-                } else if (Utilities.ATLEAST_MARSHMALLOW) {
-                    sInstance = new UserManagerCompatVM(context.getApplicationContext());
                 } else {
-                    sInstance = new UserManagerCompatVL(context.getApplicationContext());
+                    sInstance = new UserManagerCompatVNMr1(context.getApplicationContext());
                 }
             }
             return sInstance;
@@ -57,11 +51,12 @@
     public abstract List<UserHandle> getUserProfiles();
     public abstract long getSerialNumberForUser(UserHandle user);
     public abstract UserHandle getUserForSerialNumber(long serialNumber);
-    public abstract CharSequence getBadgedLabelForUser(CharSequence label, UserHandle user);
     public abstract boolean isQuietModeEnabled(UserHandle user);
     public abstract boolean isUserUnlocked(UserHandle user);
 
     public abstract boolean isDemoUser();
     public abstract boolean requestQuietModeEnabled(boolean enableQuietMode, UserHandle user);
     public abstract boolean isAnyProfileQuietModeEnabled();
+
+    public abstract boolean hasWorkProfile();
 }
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVL.java b/src/com/android/launcher3/compat/UserManagerCompatVL.java
deleted file mode 100644
index 1ff6981..0000000
--- a/src/com/android/launcher3/compat/UserManagerCompatVL.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.compat;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.ArrayMap;
-import com.android.launcher3.util.LongArrayMap;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-public class UserManagerCompatVL extends UserManagerCompat {
-    private static final String USER_CREATION_TIME_KEY = "user_creation_time_";
-
-    protected final UserManager mUserManager;
-    private final PackageManager mPm;
-    private final Context mContext;
-
-    protected LongArrayMap<UserHandle> mUsers;
-    // Create a separate reverse map as LongArrayMap.indexOfValue checks if objects are same
-    // and not {@link Object#equals}
-    protected ArrayMap<UserHandle, Long> mUserToSerialMap;
-
-    UserManagerCompatVL(Context context) {
-        mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
-        mPm = context.getPackageManager();
-        mContext = context;
-    }
-
-    @Override
-    public long getSerialNumberForUser(UserHandle user) {
-        synchronized (this) {
-            if (mUserToSerialMap != null) {
-                Long serial = mUserToSerialMap.get(user);
-                return serial == null ? 0 : serial;
-            }
-        }
-        return mUserManager.getSerialNumberForUser(user);
-    }
-
-    @Override
-    public UserHandle getUserForSerialNumber(long serialNumber) {
-        synchronized (this) {
-            if (mUsers != null) {
-                return mUsers.get(serialNumber);
-            }
-        }
-        return mUserManager.getUserForSerialNumber(serialNumber);
-    }
-
-    @Override
-    public boolean isQuietModeEnabled(UserHandle user) {
-        return false;
-    }
-
-    @Override
-    public boolean isUserUnlocked(UserHandle user) {
-        return true;
-    }
-
-    @Override
-    public boolean isDemoUser() {
-        return false;
-    }
-
-    @Override
-    public boolean requestQuietModeEnabled(boolean enableQuietMode, UserHandle user) {
-        return false;
-    }
-
-    @Override
-    public boolean isAnyProfileQuietModeEnabled() {
-        return false;
-    }
-
-    @Override
-    public void enableAndResetCache() {
-        synchronized (this) {
-            mUsers = new LongArrayMap<>();
-            mUserToSerialMap = new ArrayMap<>();
-            List<UserHandle> users = mUserManager.getUserProfiles();
-            if (users != null) {
-                for (UserHandle user : users) {
-                    long serial = mUserManager.getSerialNumberForUser(user);
-                    mUsers.put(serial, user);
-                    mUserToSerialMap.put(user, serial);
-                }
-            }
-        }
-    }
-
-    @Override
-    public List<UserHandle> getUserProfiles() {
-        synchronized (this) {
-            if (mUsers != null) {
-                return new ArrayList<>(mUserToSerialMap.keySet());
-            }
-        }
-
-        List<UserHandle> users = mUserManager.getUserProfiles();
-        return users == null ? Collections.<UserHandle>emptyList() : users;
-    }
-
-    @Override
-    public CharSequence getBadgedLabelForUser(CharSequence label, UserHandle user) {
-        if (user == null) {
-            return label;
-        }
-        return mPm.getUserBadgedLabel(label, user);
-    }
-}
-
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVM.java b/src/com/android/launcher3/compat/UserManagerCompatVM.java
deleted file mode 100644
index cf74b37..0000000
--- a/src/com/android/launcher3/compat/UserManagerCompatVM.java
+++ /dev/null
@@ -1,30 +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.compat;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.os.Build;
-import android.os.UserHandle;
-
-@TargetApi(Build.VERSION_CODES.M)
-public class UserManagerCompatVM extends UserManagerCompatVL {
-
-    UserManagerCompatVM(Context context) {
-        super(context);
-    }
-}
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVN.java b/src/com/android/launcher3/compat/UserManagerCompatVN.java
deleted file mode 100644
index 3733565..0000000
--- a/src/com/android/launcher3/compat/UserManagerCompatVN.java
+++ /dev/null
@@ -1,58 +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.compat;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.os.Build;
-import android.os.Process;
-import android.os.UserHandle;
-
-import java.util.List;
-
-@TargetApi(Build.VERSION_CODES.N)
-public class UserManagerCompatVN extends UserManagerCompatVM {
-
-    UserManagerCompatVN(Context context) {
-        super(context);
-    }
-
-    @Override
-    public boolean isQuietModeEnabled(UserHandle user) {
-        return mUserManager.isQuietModeEnabled(user);
-    }
-
-    @Override
-    public boolean isUserUnlocked(UserHandle user) {
-        return mUserManager.isUserUnlocked(user);
-    }
-
-    @Override
-    public boolean isAnyProfileQuietModeEnabled() {
-        List<UserHandle> userProfiles = getUserProfiles();
-        for (UserHandle userProfile : userProfiles) {
-            if (Process.myUserHandle().equals(userProfile)) {
-                continue;
-            }
-            if (isQuietModeEnabled(userProfile)) {
-                return true;
-            }
-        }
-        return false;
-    }
-}
-
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVNMr1.java b/src/com/android/launcher3/compat/UserManagerCompatVNMr1.java
index 3f64bc8..18d9053 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatVNMr1.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatVNMr1.java
@@ -19,16 +19,120 @@
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.os.Build;
+import android.os.Process;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.ArrayMap;
+import android.util.LongSparseArray;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 
 @TargetApi(Build.VERSION_CODES.N_MR1)
-public class UserManagerCompatVNMr1 extends UserManagerCompatVN {
+public class UserManagerCompatVNMr1 extends UserManagerCompat {
+
+    protected final UserManager mUserManager;
+
+    protected LongSparseArray<UserHandle> mUsers;
+    // Create a separate reverse map as LongSparseArray.indexOfValue checks if objects are same
+    // and not {@link Object#equals}
+    protected ArrayMap<UserHandle, Long> mUserToSerialMap;
 
     UserManagerCompatVNMr1(Context context) {
-        super(context);
+        mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+    }
+
+    @Override
+    public boolean isQuietModeEnabled(UserHandle user) {
+        return mUserManager.isQuietModeEnabled(user);
+    }
+
+    @Override
+    public boolean isUserUnlocked(UserHandle user) {
+        return mUserManager.isUserUnlocked(user);
+    }
+
+    @Override
+    public boolean isAnyProfileQuietModeEnabled() {
+        List<UserHandle> userProfiles = getUserProfiles();
+        for (UserHandle userProfile : userProfiles) {
+            if (Process.myUserHandle().equals(userProfile)) {
+                continue;
+            }
+            if (isQuietModeEnabled(userProfile)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public long getSerialNumberForUser(UserHandle user) {
+        synchronized (this) {
+            if (mUserToSerialMap != null) {
+                Long serial = mUserToSerialMap.get(user);
+                return serial == null ? 0 : serial;
+            }
+        }
+        return mUserManager.getSerialNumberForUser(user);
+    }
+
+    @Override
+    public UserHandle getUserForSerialNumber(long serialNumber) {
+        synchronized (this) {
+            if (mUsers != null) {
+                return mUsers.get(serialNumber);
+            }
+        }
+        return mUserManager.getUserForSerialNumber(serialNumber);
     }
 
     @Override
     public boolean isDemoUser() {
         return mUserManager.isDemoUser();
     }
+
+    @Override
+    public boolean requestQuietModeEnabled(boolean enableQuietMode, UserHandle user) {
+        return false;
+    }
+
+    @Override
+    public void enableAndResetCache() {
+        synchronized (this) {
+            mUsers = new LongSparseArray<>();
+            mUserToSerialMap = new ArrayMap<>();
+            List<UserHandle> users = mUserManager.getUserProfiles();
+            if (users != null) {
+                for (UserHandle user : users) {
+                    long serial = mUserManager.getSerialNumberForUser(user);
+                    mUsers.put(serial, user);
+                    mUserToSerialMap.put(user, serial);
+                }
+            }
+        }
+    }
+
+    @Override
+    public List<UserHandle> getUserProfiles() {
+        synchronized (this) {
+            if (mUsers != null) {
+                return new ArrayList<>(mUserToSerialMap.keySet());
+            }
+        }
+
+        List<UserHandle> users = mUserManager.getUserProfiles();
+        return users == null ? Collections.<UserHandle>emptyList() : users;
+    }
+
+    @Override
+    public boolean hasWorkProfile() {
+        synchronized (this) {
+            if (mUsers != null) {
+                return mUsers.size() > 1;
+            }
+        }
+        return getUserProfiles().size() > 1;
+    }
 }
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index f4c6380..45639e0 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -16,32 +16,64 @@
 
 package com.android.launcher3.config;
 
+import static androidx.core.util.Preconditions.checkNotNull;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+
+import androidx.annotation.GuardedBy;
+import androidx.annotation.Keep;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.launcher3.Utilities;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
 /**
  * Defines a set of flags used to control various launcher behaviors.
  *
- * All the flags should be defined here with appropriate default values. To override a value,
- * redefine it in {@link FeatureFlags}.
+ * <p>All the flags should be defined here with appropriate default values.
  *
- * This class is kept package-private to prevent direct access.
+ * <p>This class is kept package-private to prevent direct access.
  */
+@Keep
 abstract class BaseFlags {
 
-    BaseFlags() {}
+    private static final Object sLock = new Object();
+    @GuardedBy("sLock")
+    private static final List<TogglableFlag> sFlags = new ArrayList<>();
+
+    static final String FLAGS_PREF_NAME = "featureFlags";
+
+    BaseFlags() {
+        throw new UnsupportedOperationException("Don't instantiate BaseFlags");
+    }
+
+    public static boolean showFlagTogglerUi(Context context) {
+        return Utilities.IS_DEBUG_DEVICE && Utilities.isDevelopersOptionsEnabled(context);
+    }
 
     public static final boolean IS_DOGFOOD_BUILD = false;
-    public static final String AUTHORITY = "com.android.launcher3.settings".intern();
 
-    // When enabled allows to use any point on the fast scrollbar to start dragging.
-    public static final boolean LAUNCHER3_DIRECT_SCROLL = true;
     // When enabled the promise icon is visible in all apps while installation an app.
     public static final boolean LAUNCHER3_PROMISE_APPS_IN_ALL_APPS = false;
-    // When enabled allows use of spring motions on the icons.
-    public static final boolean LAUNCHER3_SPRING_ICONS = true;
 
-    // Feature flag to enable moving the QSB on the 0th screen of the workspace.
+    // Enable moving the QSB on the 0th screen of the workspace
     public static final boolean QSB_ON_FIRST_SCREEN = true;
-    // When enabled the all-apps icon is not added to the hotseat.
-    public static final boolean NO_ALL_APPS_ICON = true;
+
+    public static final TogglableFlag EXAMPLE_FLAG = new TogglableFlag("EXAMPLE_FLAG", true,
+            "An example flag that doesn't do anything. Useful for testing");
+
+    //Feature flag to enable pulling down navigation shade from workspace.
+    public static final boolean PULL_DOWN_STATUS_BAR = true;
 
     // When true, custom widgets are loaded using CustomWidgetParser.
     public static final boolean ENABLE_CUSTOM_WIDGETS = false;
@@ -55,4 +87,194 @@
     // When true, overview shows screenshots in the orientation they were taken rather than
     // trying to make them fit the orientation the device is in.
     public static final boolean OVERVIEW_USE_SCREENSHOT_ORIENTATION = true;
+
+    /**
+     * Feature flag to handle define config changes dynamically instead of killing the process.
+     */
+    public static final TogglableFlag APPLY_CONFIG_AT_RUNTIME = new TogglableFlag(
+            "APPLY_CONFIG_AT_RUNTIME", true, "Apply display changes dynamically");
+
+    public static final TogglableFlag QUICKSTEP_SPRINGS = new TogglableFlag("QUICKSTEP_SPRINGS",
+            false, "Enable springs for quickstep animations");
+
+    public static final TogglableFlag ADAPTIVE_ICON_WINDOW_ANIM = new TogglableFlag(
+            "ADAPTIVE_ICON_WINDOW_ANIM", true,
+            "Use adaptive icons for window animations.");
+
+    public static final TogglableFlag ENABLE_QUICKSTEP_LIVE_TILE = new TogglableFlag(
+            "ENABLE_QUICKSTEP_LIVE_TILE", false, "Enable live tile in Quickstep overview");
+
+    public static final TogglableFlag ENABLE_HINTS_IN_OVERVIEW = new TogglableFlag(
+            "ENABLE_HINTS_IN_OVERVIEW", false,
+            "Show chip hints and gleams on the overview screen");
+
+    public static final TogglableFlag FAKE_LANDSCAPE_UI = new TogglableFlag(
+            "FAKE_LANDSCAPE_UI", false,
+            "Rotate launcher UI instead of using transposed layout");
+
+    public static void initialize(Context context) {
+        // Avoid the disk read for user builds
+        if (Utilities.IS_DEBUG_DEVICE) {
+            synchronized (sLock) {
+                for (TogglableFlag flag : sFlags) {
+                    flag.initialize(context);
+                }
+            }
+        }
+    }
+
+    static List<TogglableFlag> getTogglableFlags() {
+        // By Java Language Spec 12.4.2
+        // https://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.2, the
+        // TogglableFlag instances on BaseFlags will be created before those on the FeatureFlags
+        // subclass. This code handles flags that are redeclared in FeatureFlags, ensuring the
+        // FeatureFlags one takes priority.
+        SortedMap<String, TogglableFlag> flagsByKey = new TreeMap<>();
+        synchronized (sLock) {
+            for (TogglableFlag flag : sFlags) {
+                flagsByKey.put(flag.key, flag);
+            }
+        }
+        return new ArrayList<>(flagsByKey.values());
+    }
+
+    public static class TogglableFlag {
+        private final String key;
+        private final boolean defaultValue;
+        private final String description;
+        private boolean currentValue;
+
+        TogglableFlag(
+                String key,
+                boolean defaultValue,
+                String description) {
+            this.key = checkNotNull(key);
+            this.currentValue = this.defaultValue = defaultValue;
+            this.description = checkNotNull(description);
+            synchronized (sLock) {
+                sFlags.add(this);
+            }
+        }
+
+        /** Set the value of this flag. This should only be used in tests. */
+        @VisibleForTesting
+        void setForTests(boolean value) {
+            currentValue = value;
+        }
+
+        @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+        public String getKey() {
+            return key;
+        }
+        void initialize(Context context) {
+            currentValue = getFromStorage(context, defaultValue);
+        }
+
+        public void updateStorage(Context context, boolean value) {
+            SharedPreferences.Editor editor = context.getSharedPreferences(FLAGS_PREF_NAME,
+                    Context.MODE_PRIVATE).edit();
+            if (value == defaultValue) {
+                editor.remove(key).apply();
+            } else {
+                editor.putBoolean(key, value).apply();
+            }
+        }
+
+        boolean getFromStorage(Context context, boolean defaultValue) {
+            return context.getSharedPreferences(FLAGS_PREF_NAME, Context.MODE_PRIVATE)
+                    .getBoolean(key, defaultValue);
+        }
+
+        boolean getDefaultValue() {
+            return defaultValue;
+        }
+
+        /** Returns the value of the flag at process start, including any overrides present. */
+        public boolean get() {
+            return currentValue;
+        }
+
+        String getDescription() {
+            return description;
+        }
+
+        @Override
+        public String toString() {
+            return "TogglableFlag{"
+                    + "key=" + key + ", "
+                    + "defaultValue=" + defaultValue + ", "
+                    + "description=" + description
+                    + "}";
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o == this) {
+                return true;
+            }
+            if (o instanceof TogglableFlag) {
+                TogglableFlag that = (TogglableFlag) o;
+                return (this.key.equals(that.getKey()))
+                        && (this.defaultValue == that.getDefaultValue())
+                        && (this.description.equals(that.getDescription()));
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            int h$ = 1;
+            h$ *= 1000003;
+            h$ ^= key.hashCode();
+            h$ *= 1000003;
+            h$ ^= defaultValue ? 1231 : 1237;
+            h$ *= 1000003;
+            h$ ^= description.hashCode();
+            return h$;
+        }
+    }
+
+    /**
+     * Stores the FeatureFlag's value in Settings.Global instead of our SharedPrefs.
+     * This is useful if we want to be able to control this flag from another process.
+     */
+    public static final class ToggleableGlobalSettingsFlag extends TogglableFlag {
+        private ContentResolver contentResolver;
+
+        ToggleableGlobalSettingsFlag(String key, boolean defaultValue, String description) {
+            super(key, defaultValue, description);
+        }
+
+        @Override
+        public void initialize(Context context) {
+            contentResolver = context.getContentResolver();
+            contentResolver.registerContentObserver(Settings.Global.getUriFor(getKey()), true,
+                    new ContentObserver(new Handler(Looper.getMainLooper())) {
+                        @Override
+                        public void onChange(boolean selfChange) {
+                            superInitialize(context);
+                    }});
+            superInitialize(context);
+        }
+
+        private void superInitialize(Context context) {
+            super.initialize(context);
+        }
+
+        @Override
+        public void updateStorage(Context context, boolean value) {
+            if (contentResolver == null) {
+                return;
+            }
+            Settings.Global.putInt(contentResolver, getKey(), value ? 1 : 0);
+        }
+
+        @Override
+        boolean getFromStorage(Context context, boolean defaultValue) {
+            if (contentResolver == null) {
+                return defaultValue;
+            }
+            return Settings.Global.getInt(contentResolver, getKey(), defaultValue ? 1 : 0) == 1;
+        }
+    }
 }
diff --git a/src/com/android/launcher3/config/FlagTogglerPrefUi.java b/src/com/android/launcher3/config/FlagTogglerPrefUi.java
new file mode 100644
index 0000000..5ecb186
--- /dev/null
+++ b/src/com/android/launcher3/config/FlagTogglerPrefUi.java
@@ -0,0 +1,149 @@
+/*
+ * 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.config;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Process;
+import android.text.Html;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.Toast;
+
+import com.android.launcher3.R;
+import com.android.launcher3.config.BaseFlags.TogglableFlag;
+
+import androidx.preference.PreferenceDataStore;
+import androidx.preference.PreferenceFragment;
+import androidx.preference.PreferenceGroup;
+import androidx.preference.SwitchPreference;
+
+/**
+ * Dev-build only UI allowing developers to toggle flag settings. See {@link FeatureFlags}.
+ */
+public final class FlagTogglerPrefUi {
+
+    private static final String TAG = "FlagTogglerPrefFrag";
+
+    private final PreferenceFragment mFragment;
+    private final Context mContext;
+    private final SharedPreferences mSharedPreferences;
+
+    private final PreferenceDataStore mDataStore = new PreferenceDataStore() {
+
+        @Override
+        public void putBoolean(String key, boolean value) {
+            for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
+                if (flag.getKey().equals(key)) {
+                    boolean prevValue = flag.get();
+                    flag.updateStorage(mContext, value);
+                    updateMenu();
+                    if (flag.get() != prevValue) {
+                        Toast.makeText(mContext, "Flag applied", Toast.LENGTH_SHORT).show();
+                    }
+                }
+            }
+        }
+
+        @Override
+        public boolean getBoolean(String key, boolean defaultValue) {
+            for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
+                if (flag.getKey().equals(key)) {
+                    return flag.getFromStorage(mContext, defaultValue);
+                }
+            }
+            return defaultValue;
+        }
+    };
+
+    public FlagTogglerPrefUi(PreferenceFragment fragment) {
+        mFragment = fragment;
+        mContext = fragment.getActivity();
+        mSharedPreferences = mContext.getSharedPreferences(
+                FeatureFlags.FLAGS_PREF_NAME, Context.MODE_PRIVATE);
+    }
+
+    public void applyTo(PreferenceGroup parent) {
+        // For flag overrides we only want to store when the engineer chose to override the
+        // flag with a different value than the default. That way, when we flip flags in
+        // future, engineers will pick up the new value immediately. To accomplish this, we use a
+        // custom preference data store.
+        for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
+            SwitchPreference switchPreference = new SwitchPreference(mContext);
+            switchPreference.setKey(flag.getKey());
+            switchPreference.setDefaultValue(flag.getDefaultValue());
+            switchPreference.setChecked(getFlagStateFromSharedPrefs(flag));
+            switchPreference.setTitle(flag.getKey());
+            updateSummary(switchPreference, flag);
+            switchPreference.setPreferenceDataStore(mDataStore);
+            parent.addPreference(switchPreference);
+        }
+        updateMenu();
+    }
+
+    /**
+     * Updates the summary to show the description and whether the flag overrides the default value.
+     */
+    private void updateSummary(SwitchPreference switchPreference, TogglableFlag flag) {
+        String onWarning = flag.getDefaultValue() ? "" : "<b>OVERRIDDEN</b><br>";
+        String offWarning = flag.getDefaultValue() ? "<b>OVERRIDDEN</b><br>" : "";
+        switchPreference.setSummaryOn(Html.fromHtml(onWarning + flag.getDescription()));
+        switchPreference.setSummaryOff(Html.fromHtml(offWarning + flag.getDescription()));
+    }
+
+    private void updateMenu() {
+        mFragment.setHasOptionsMenu(anyChanged());
+        mFragment.getActivity().invalidateOptionsMenu();
+    }
+
+    public void onCreateOptionsMenu(Menu menu) {
+        if (anyChanged()) {
+            menu.add(0, R.id.menu_apply_flags, 0, "Apply")
+                    .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+        }
+    }
+
+    public void onOptionsItemSelected(MenuItem item) {
+        if (item.getItemId() == R.id.menu_apply_flags) {
+            mSharedPreferences.edit().commit();
+            Log.e(TAG,
+                    "Killing launcher process " + Process.myPid() + " to apply new flag values");
+            System.exit(0);
+        }
+    }
+
+    public void onStop() {
+        if (anyChanged()) {
+            Toast.makeText(mContext, "Flag won't be applied until you restart launcher",
+                    Toast.LENGTH_LONG).show();
+        }
+    }
+
+    private boolean getFlagStateFromSharedPrefs(TogglableFlag flag) {
+        return mDataStore.getBoolean(flag.getKey(), flag.getDefaultValue());
+    }
+
+    private boolean anyChanged() {
+        for (TogglableFlag flag : FeatureFlags.getTogglableFlags()) {
+            if (getFlagStateFromSharedPrefs(flag) != flag.get()) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/dot/DotInfo.java b/src/com/android/launcher3/dot/DotInfo.java
new file mode 100644
index 0000000..4ff0539
--- /dev/null
+++ b/src/com/android/launcher3/dot/DotInfo.java
@@ -0,0 +1,86 @@
+/*
+ * 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.dot;
+
+import com.android.launcher3.notification.NotificationInfo;
+import com.android.launcher3.notification.NotificationKeyData;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Contains data to be used for a notification dot.
+ */
+public class DotInfo {
+
+    public static final int MAX_COUNT = 999;
+
+    /**
+     * The keys of the notifications that this dot represents. These keys can later be
+     * used to retrieve {@link NotificationInfo}'s.
+     */
+    private final List<NotificationKeyData> mNotificationKeys = new ArrayList<>();
+
+    /**
+     * The current sum of the counts in {@link #mNotificationKeys},
+     * updated whenever a key is added or removed.
+     */
+    private int mTotalCount;
+
+    /**
+     * Returns whether the notification was added or its count changed.
+     */
+    public boolean addOrUpdateNotificationKey(NotificationKeyData notificationKey) {
+        int indexOfPrevKey = mNotificationKeys.indexOf(notificationKey);
+        NotificationKeyData prevKey = indexOfPrevKey == -1 ? null
+                : mNotificationKeys.get(indexOfPrevKey);
+        if (prevKey != null) {
+            if (prevKey.count == notificationKey.count) {
+                return false;
+            }
+            // Notification was updated with a new count.
+            mTotalCount -= prevKey.count;
+            mTotalCount += notificationKey.count;
+            prevKey.count = notificationKey.count;
+            return true;
+        }
+        boolean added = mNotificationKeys.add(notificationKey);
+        if (added) {
+            mTotalCount += notificationKey.count;
+        }
+        return added;
+    }
+
+    /**
+     * Returns whether the notification was removed (false if it didn't exist).
+     */
+    public boolean removeNotificationKey(NotificationKeyData notificationKey) {
+        boolean removed = mNotificationKeys.remove(notificationKey);
+        if (removed) {
+            mTotalCount -= notificationKey.count;
+        }
+        return removed;
+    }
+
+    public List<NotificationKeyData> getNotificationKeys() {
+        return mNotificationKeys;
+    }
+
+    public int getNotificationCount() {
+        return Math.min(mTotalCount, MAX_COUNT);
+    }
+}
diff --git a/src/com/android/launcher3/dot/FolderDotInfo.java b/src/com/android/launcher3/dot/FolderDotInfo.java
new file mode 100644
index 0000000..54800a0
--- /dev/null
+++ b/src/com/android/launcher3/dot/FolderDotInfo.java
@@ -0,0 +1,60 @@
+/*
+ * 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.dot;
+
+import android.view.ViewDebug;
+
+import com.android.launcher3.Utilities;
+
+/**
+ * Subclass of DotInfo that only contains the dot count, which is
+ * the sum of all the Folder's items' notifications (each counts as 1).
+ */
+public class FolderDotInfo extends DotInfo {
+
+    private static final int MIN_COUNT = 0;
+
+    private int mNumNotifications;
+
+    public void addDotInfo(DotInfo dotToAdd) {
+        if (dotToAdd == null) {
+            return;
+        }
+        mNumNotifications += dotToAdd.getNotificationKeys().size();
+        mNumNotifications = Utilities.boundToRange(
+                mNumNotifications, MIN_COUNT, DotInfo.MAX_COUNT);
+    }
+
+    public void subtractDotInfo(DotInfo dotToSubtract) {
+        if (dotToSubtract == null) {
+            return;
+        }
+        mNumNotifications -= dotToSubtract.getNotificationKeys().size();
+        mNumNotifications = Utilities.boundToRange(
+                mNumNotifications, MIN_COUNT, DotInfo.MAX_COUNT);
+    }
+
+    @Override
+    public int getNotificationCount() {
+        return mNumNotifications;
+    }
+
+    @ViewDebug.ExportedProperty(category = "launcher")
+    public boolean hasDot() {
+        return mNumNotifications > 0;
+    }
+}
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index 278eefd..a72089d 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -32,6 +32,7 @@
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
+import android.os.AsyncTask;
 import android.os.Build;
 import android.os.Bundle;
 import android.view.MotionEvent;
@@ -46,19 +47,23 @@
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherAppWidgetHost;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.LauncherModel;
 import com.android.launcher3.R;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.LauncherAppsCompatVO;
 import com.android.launcher3.model.WidgetItem;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 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.LooperExecutor;
+import com.android.launcher3.views.BaseDragLayer;
 import com.android.launcher3.widget.PendingAddShortcutInfo;
 import com.android.launcher3.widget.PendingAddWidgetInfo;
 import com.android.launcher3.widget.WidgetHostViewLoader;
 import com.android.launcher3.widget.WidgetImageView;
 
+import java.util.function.Supplier;
+
 @TargetApi(Build.VERSION_CODES.O)
 public class AddItemActivity extends BaseActivity implements OnLongClickListener, OnTouchListener {
 
@@ -78,7 +83,6 @@
     // Widget request specific options.
     private LauncherAppWidgetHost mAppWidgetHost;
     private AppWidgetManagerCompat mAppWidgetManager;
-    private PendingAddWidgetInfo mPendingWidgetInfo;
     private int mPendingBindWidgetId;
     private Bundle mWidgetOptions;
 
@@ -189,10 +193,9 @@
     private void setupShortcut() {
         PinShortcutRequestActivityInfo shortcutInfo =
                 new PinShortcutRequestActivityInfo(mRequest, this);
-        WidgetItem item = new WidgetItem(shortcutInfo);
         mWidgetCell.getWidgetView().setTag(new PendingAddShortcutInfo(shortcutInfo));
-        mWidgetCell.applyFromCellItem(item, mApp.getWidgetCache());
-        mWidgetCell.ensurePreview();
+        applyWidgetItemAsync(
+                () -> new WidgetItem(shortcutInfo, mApp.getIconCache(), getPackageManager()));
     }
 
     private boolean setupWidget() {
@@ -207,18 +210,32 @@
         mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);
         mAppWidgetHost = new LauncherAppWidgetHost(this);
 
-        mPendingWidgetInfo = new PendingAddWidgetInfo(widgetInfo);
-        mPendingWidgetInfo.spanX = Math.min(mIdp.numColumns, widgetInfo.spanX);
-        mPendingWidgetInfo.spanY = Math.min(mIdp.numRows, widgetInfo.spanY);
-        mWidgetOptions = WidgetHostViewLoader.getDefaultOptionsForWidget(this, mPendingWidgetInfo);
+        PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(widgetInfo);
+        pendingInfo.spanX = Math.min(mIdp.numColumns, widgetInfo.spanX);
+        pendingInfo.spanY = Math.min(mIdp.numRows, widgetInfo.spanY);
+        mWidgetOptions = WidgetHostViewLoader.getDefaultOptionsForWidget(this, pendingInfo);
+        mWidgetCell.getWidgetView().setTag(pendingInfo);
 
-        WidgetItem item = new WidgetItem(widgetInfo, getPackageManager(), mIdp);
-        mWidgetCell.getWidgetView().setTag(mPendingWidgetInfo);
-        mWidgetCell.applyFromCellItem(item, mApp.getWidgetCache());
-        mWidgetCell.ensurePreview();
+        applyWidgetItemAsync(() -> new WidgetItem(widgetInfo, mIdp, mApp.getIconCache()));
         return true;
     }
 
+    private void applyWidgetItemAsync(final Supplier<WidgetItem> itemProvider) {
+        new AsyncTask<Void, Void, WidgetItem>() {
+            @Override
+            protected WidgetItem doInBackground(Void... voids) {
+                return itemProvider.get();
+            }
+
+            @Override
+            protected void onPostExecute(WidgetItem item) {
+                mWidgetCell.applyFromCellItem(item, mApp.getWidgetCache());
+                mWidgetCell.ensurePreview();
+            }
+        }.executeOnExecutor(new LooperExecutor(LauncherModel.getWorkerLooper()));
+        // TODO: Create a worker looper executor and reuse that everywhere.
+    }
+
     /**
      * Called when the cancel button is clicked.
      */
@@ -232,8 +249,7 @@
      */
     public void onPlaceAutomaticallyClick(View v) {
         if (mRequest.getRequestType() == PinItemRequest.REQUEST_TYPE_SHORTCUT) {
-            InstallShortcutReceiver.queueShortcut(
-                    new ShortcutInfoCompat(mRequest.getShortcutInfo()), this);
+            InstallShortcutReceiver.queueShortcut(mRequest.getShortcutInfo(), this);
             logCommand(Action.Command.CONFIRM);
             mRequest.accept();
             finish();
@@ -298,6 +314,11 @@
                 .getInt(STATE_EXTRA_WIDGET_ID, mPendingBindWidgetId);
     }
 
+    @Override
+    public BaseDragLayer getDragLayer() {
+        throw new UnsupportedOperationException();
+    }
+
     private void logCommand(int command) {
         getUserEventDispatcher().dispatchUserEvent(newLauncherEvent(
                 newCommandAction(command),
diff --git a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
index 1e84b41..c719c1c 100644
--- a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
+++ b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
@@ -37,6 +37,7 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.states.InternalStateHandler;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.widget.PendingItemDragHelper;
 
 import java.util.UUID;
@@ -106,6 +107,10 @@
     }
 
     protected boolean onDragStart(DragEvent event) {
+        return onDragStart(event, this);
+    }
+
+    protected boolean onDragStart(DragEvent event, DragOptions.PreDragCondition preDragCondition) {
         ClipDescription desc =  event.getClipDescription();
         if (desc == null || !desc.hasMimeType(getMimeType())) {
             Log.e(TAG, "Someone started a dragAndDrop before us.");
@@ -115,7 +120,7 @@
         Point downPos = new Point((int) event.getX(), (int) event.getY());
         DragOptions options = new DragOptions();
         options.systemDndStartPoint = downPos;
-        options.preDragCondition = this;
+        options.preDragCondition = preDragCondition;
 
         // We use drag event position as the screenPos for the preview image. Since mPreviewRect
         // already includes the view position relative to the drag event on the source window,
@@ -123,7 +128,7 @@
         // across windows, using drag position here give a good estimate for relative position
         // to source window.
         createDragHelper().startDrag(new Rect(mPreviewRect),
-                mPreviewBitmapWidth, mPreviewViewWidth, downPos,  this, options);
+                mPreviewBitmapWidth, mPreviewViewWidth, downPos, this, options);
         mDragStartTime = SystemClock.uptimeMillis();
         return true;
     }
@@ -132,6 +137,9 @@
 
     @Override
     public boolean shouldStartDrag(double distanceDragged) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_DRAG_TAG, "BIDL.shouldStartDrag");
+        }
         // Stay in pre-drag mode, if workspace is locked.
         return !mLauncher.isWorkspaceLocked();
     }
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 8a216fc..72a1abb 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -16,28 +16,33 @@
 
 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 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.os.IBinder;
+import android.util.Log;
 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 com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.accessibility.DragViewStateAnnouncer;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.util.TouchController;
@@ -145,6 +150,7 @@
 
         // Hide soft keyboard, if visible
         UiThreadHelper.hideKeyboardAsync(mLauncher, mWindowToken);
+        AbstractFloatingView.closeOpenViews(mLauncher, false, TYPE_DISCOVERY_BOUNCE);
 
         mOptions = options;
         if (mOptions.systemDndStartPoint != null) {
@@ -224,6 +230,12 @@
         }
     }
 
+    public void addFirstFrameAnimationHelper(ValueAnimator anim) {
+        if (mDragObject != null && mDragObject.dragView != null) {
+            mDragObject.dragView.mFirstFrameAnimatorHelper.addTo(anim);
+        }
+    }
+
     /**
      * Call this from a drag source view like this:
      *
@@ -275,7 +287,7 @@
         // Cancel the current drag if we are removing an app that we are dragging
         if (mDragObject != null) {
             ItemInfo dragInfo = mDragObject.dragInfo;
-            if (dragInfo instanceof ShortcutInfo) {
+            if (dragInfo instanceof WorkspaceItemInfo) {
                 ComponentName cn = dragInfo.getTargetComponent();
                 if (cn != null && matcher.matches(dragInfo, cn)) {
                     cancelDrag();
@@ -386,7 +398,7 @@
     @Override
     public void onDriverDragEnd(float x, float y) {
         DropTarget dropTarget;
-        Runnable flingAnimation = mFlingToDeleteHelper.getFlingAnimation(mDragObject);
+        Runnable flingAnimation = mFlingToDeleteHelper.getFlingAnimation(mDragObject, mOptions);
         if (flingAnimation != null) {
             dropTarget = mFlingToDeleteHelper.getDropTarget();
         } else {
@@ -462,6 +474,10 @@
     }
 
     private void handleMoveEvent(int x, int y) {
+        if (TestProtocol.sDebugTracing) {
+            android.util.Log.d(TestProtocol.NO_DRAG_TAG,
+                    "handleMoveEvent 1");
+        }
         mDragObject.dragView.move(x, y);
 
         // Drop on someone?
@@ -476,8 +492,22 @@
         mLastTouch[0] = x;
         mLastTouch[1] = y;
 
+        if (TestProtocol.sDebugTracing) {
+           Log.d(TestProtocol.NO_DRAG_TAG,
+                    "handleMoveEvent Conditions " +
+                            mIsInPreDrag + ", " +
+                            (mIsInPreDrag && mOptions.preDragCondition != null) + ", " +
+                            (mIsInPreDrag && mOptions.preDragCondition != null
+                                    && mOptions.preDragCondition.shouldStartDrag(
+                                    mDistanceSinceScroll)));
+        }
+
         if (mIsInPreDrag && mOptions.preDragCondition != null
                 && mOptions.preDragCondition.shouldStartDrag(mDistanceSinceScroll)) {
+            if (TestProtocol.sDebugTracing) {
+                android.util.Log.d(TestProtocol.NO_DRAG_TAG,
+                        "handleMoveEvent 2");
+            }
             callOnDragStart();
         }
     }
@@ -515,6 +545,10 @@
      * Call this from a drag source view.
      */
     public boolean onControllerTouchEvent(MotionEvent ev) {
+        if (TestProtocol.sDebugTracing) {
+            android.util.Log.d(TestProtocol.NO_DRAG_TAG,
+                    "onControllerTouchEvent");
+        }
         if (mDragDriver == null || mOptions == null || mOptions.isAccessibleDrag) {
             return false;
         }
@@ -668,5 +702,4 @@
     public void removeDropTarget(DropTarget target) {
         mDropTargets.remove(target);
     }
-
 }
diff --git a/src/com/android/launcher3/dragndrop/DragDriver.java b/src/com/android/launcher3/dragndrop/DragDriver.java
index d8a3024..84fc94d 100644
--- a/src/com/android/launcher3/dragndrop/DragDriver.java
+++ b/src/com/android/launcher3/dragndrop/DragDriver.java
@@ -19,8 +19,8 @@
 import android.content.Context;
 import android.view.DragEvent;
 import android.view.MotionEvent;
+
 import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.Utilities;
 
 /**
  * Base class for driving a drag/drop operation.
@@ -83,7 +83,7 @@
 
     public static DragDriver create(Context context, DragController dragController,
             DragObject dragObject, DragOptions options) {
-        if (Utilities.ATLEAST_NOUGAT && options.systemDndStartPoint != null) {
+        if (options.systemDndStartPoint != null) {
             return new SystemDragDriver(dragController, context, dragObject);
         } else {
             return new InternalDragDriver(dragController);
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 6d2d3cb..6ba015b 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -17,6 +17,10 @@
 
 package com.android.launcher3.dragndrop;
 
+import static android.view.View.MeasureSpec.EXACTLY;
+import static android.view.View.MeasureSpec.getMode;
+import static android.view.View.MeasureSpec.getSize;
+
 import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
 
 import android.animation.Animator;
@@ -29,12 +33,14 @@
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.util.AttributeSet;
+import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.animation.Interpolator;
+import android.widget.FrameLayout;
 import android.widget.TextView;
 
 import com.android.launcher3.AbstractFloatingView;
@@ -42,17 +48,18 @@
 import com.android.launcher3.DropTargetBar;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
+import com.android.launcher3.graphics.RotationMode;
 import com.android.launcher3.ShortcutAndWidgetContainer;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
-import com.android.launcher3.graphics.ViewScrim;
 import com.android.launcher3.graphics.WorkspaceAndHotseatScrim;
 import com.android.launcher3.keyboard.ViewGroupFocusHelper;
 import com.android.launcher3.uioverrides.UiFactory;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.views.BaseDragLayer;
+import com.android.launcher3.views.Transposable;
 
 import java.util.ArrayList;
 
@@ -64,8 +71,7 @@
     public static final int ALPHA_INDEX_OVERLAY = 0;
     public static final int ALPHA_INDEX_LAUNCHER_LOAD = 1;
     public static final int ALPHA_INDEX_TRANSITIONS = 2;
-    public static final int ALPHA_INDEX_SWIPE_UP = 3;
-    private static final int ALPHA_CHANNEL_COUNT = 4;
+    private static final int ALPHA_CHANNEL_COUNT = 3;
 
     public static final int ANIMATION_END_DISAPPEAR = 0;
     public static final int ANIMATION_END_REMAIN_VISIBLE = 2;
@@ -124,25 +130,6 @@
         return mDragController.dispatchKeyEvent(event) || super.dispatchKeyEvent(event);
     }
 
-    @Override
-    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
-        ViewScrim scrim = ViewScrim.get(child);
-        if (scrim != null) {
-            scrim.draw(canvas, getWidth(), getHeight());
-        }
-        return super.drawChild(canvas, child, drawingTime);
-    }
-
-    @Override
-    protected boolean findActiveController(MotionEvent ev) {
-        if (mActivity.getStateManager().getState().disableInteraction) {
-            // You Shall Not Pass!!!
-            mActiveController = null;
-            return true;
-        }
-        return super.findActiveController(ev);
-    }
-
     private boolean isEventOverAccessibleDropTargetBar(MotionEvent ev) {
         return isInAccessibleDrag() && isEventOverView(mActivity.getDropTargetBar(), ev);
     }
@@ -271,10 +258,10 @@
         Rect r = new Rect();
         getViewRectRelativeToSelf(dragView, r);
 
-        int coord[] = new int[2];
+        float coord[] = new float[2];
         float childScale = child.getScaleX();
-        coord[0] = lp.x + (int) (child.getMeasuredWidth() * (1 - childScale) / 2);
-        coord[1] = lp.y + (int) (child.getMeasuredHeight() * (1 - childScale) / 2);
+        coord[0] = lp.x + (child.getMeasuredWidth() * (1 - childScale) / 2);
+        coord[1] = lp.y + (child.getMeasuredHeight() * (1 - childScale) / 2);
 
         // Since the child hasn't necessarily been laid out, we force the lp to be updated with
         // the correct coordinates (above) and use these to determine the final location
@@ -282,8 +269,8 @@
         // We need to account for the scale of the child itself, as the above only accounts for
         // for the scale in parents.
         scale *= childScale;
-        int toX = coord[0];
-        int toY = coord[1];
+        int toX = Math.round(coord[0]);
+        int toY = Math.round(coord[1]);
         float toScale = scale;
         if (child instanceof TextView) {
             TextView tv = (TextView) child;
@@ -562,4 +549,145 @@
     public WorkspaceAndHotseatScrim getScrim() {
         return mScrim;
     }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        RotationMode rotation = mActivity.getRotationMode();
+        int count = getChildCount();
+
+        if (!rotation.isTransposed
+                || getMode(widthMeasureSpec) != EXACTLY
+                || getMode(heightMeasureSpec) != EXACTLY) {
+
+            for (int i = 0; i < count; i++) {
+                final View child = getChildAt(i);
+                child.setRotation(rotation.surfaceRotation);
+            }
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        } else {
+
+            for (int i = 0; i < count; i++) {
+                final View child = getChildAt(i);
+                if (child.getVisibility() == GONE) {
+                    continue;
+                }
+                if (!(child instanceof Transposable)) {
+                    measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
+                } else {
+                    measureChildWithMargins(child, heightMeasureSpec, 0, widthMeasureSpec, 0);
+
+                    child.setPivotX(child.getMeasuredWidth() / 2);
+                    child.setPivotY(child.getMeasuredHeight() / 2);
+                    child.setRotation(rotation.surfaceRotation);
+                }
+            }
+            setMeasuredDimension(getSize(widthMeasureSpec), getSize(heightMeasureSpec));
+        }
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        RotationMode rotation = mActivity.getRotationMode();
+        if (!rotation.isTransposed) {
+            super.onLayout(changed, left, top, right, bottom);
+            return;
+        }
+
+        final int count = getChildCount();
+
+        final int parentWidth = right - left;
+        final int parentHeight = bottom - top;
+
+        for (int i = 0; i < count; i++) {
+            final View child = getChildAt(i);
+            if (child.getVisibility() == GONE) {
+                continue;
+            }
+
+            final FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) child.getLayoutParams();
+
+            if (lp instanceof LayoutParams) {
+                final LayoutParams dlp = (LayoutParams) lp;
+                if (dlp.customPosition) {
+                    child.layout(dlp.x, dlp.y, dlp.x + dlp.width, dlp.y + dlp.height);
+                    continue;
+                }
+            }
+
+            final int width = child.getMeasuredWidth();
+            final int height = child.getMeasuredHeight();
+
+            int childLeft;
+            int childTop;
+
+            int gravity = lp.gravity;
+            if (gravity == -1) {
+                gravity = Gravity.TOP | Gravity.START;
+            }
+
+            final int layoutDirection = getLayoutDirection();
+
+            int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
+
+            if (child instanceof Transposable) {
+                absoluteGravity = rotation.toNaturalGravity(absoluteGravity);
+
+                switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
+                    case Gravity.CENTER_HORIZONTAL:
+                        childTop = (parentHeight - height) / 2 +
+                                lp.topMargin - lp.bottomMargin;
+                        break;
+                    case Gravity.RIGHT:
+                        childTop = width / 2 + lp.rightMargin - height / 2;
+                        break;
+                    case Gravity.LEFT:
+                    default:
+                        childTop = parentHeight - lp.leftMargin - width / 2 - height / 2;
+                }
+
+                switch (absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK) {
+                    case Gravity.CENTER_VERTICAL:
+                        childLeft = (parentWidth - width) / 2 +
+                                lp.leftMargin - lp.rightMargin;
+                        break;
+                    case Gravity.BOTTOM:
+                        childLeft = parentWidth - width / 2 - height / 2 - lp.bottomMargin;
+                        break;
+                    case Gravity.TOP:
+                    default:
+                        childLeft = height / 2 - width / 2 + lp.topMargin;
+                }
+            } else {
+                switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
+                    case Gravity.CENTER_HORIZONTAL:
+                        childLeft = (parentWidth - width) / 2 +
+                                lp.leftMargin - lp.rightMargin;
+                        break;
+                    case Gravity.RIGHT:
+                        childLeft = parentWidth - width - lp.rightMargin;
+                        break;
+                    case Gravity.LEFT:
+                    default:
+                        childLeft = lp.leftMargin;
+                }
+
+                switch (absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK) {
+                    case Gravity.TOP:
+                        childTop = lp.topMargin;
+                        break;
+                    case Gravity.CENTER_VERTICAL:
+                        childTop = (parentHeight - height) / 2 +
+                                lp.topMargin - lp.bottomMargin;
+                        break;
+                    case Gravity.BOTTOM:
+                        childTop = parentHeight - height - lp.bottomMargin;
+                        break;
+                    default:
+                        childTop = lp.topMargin;
+                }
+            }
+
+            child.layout(childLeft, childTop, childLeft + width, childTop + height);
+        }
+    }
 }
diff --git a/src/com/android/launcher3/dragndrop/DragOptions.java b/src/com/android/launcher3/dragndrop/DragOptions.java
index f108f8b..2d19f36 100644
--- a/src/com/android/launcher3/dragndrop/DragOptions.java
+++ b/src/com/android/launcher3/dragndrop/DragOptions.java
@@ -37,6 +37,8 @@
     /** Scale of the icons over the workspace icon size. */
     public float intrinsicIconScaleFactor = 1f;
 
+    public boolean isFlingToDelete;
+
     /**
      * Specifies a condition that must be met before DragListener#onDragStart() is called.
      * By default, there is no condition and onDragStart() is called immediately following
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 551567a..09c5e5b 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -16,13 +16,14 @@
 
 package com.android.launcher3.dragndrop;
 
+import static com.android.launcher3.Utilities.getBadge;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.FloatArrayEvaluator;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.annotation.TargetApi;
-import android.content.pm.LauncherActivityInfo;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -35,52 +36,39 @@
 import android.graphics.drawable.AdaptiveIconDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
-import android.graphics.drawable.InsetDrawable;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
-import android.support.animation.FloatPropertyCompat;
-import android.support.animation.SpringAnimation;
-import android.support.animation.SpringForce;
 import android.view.View;
 
 import com.android.launcher3.FastBitmapDrawable;
 import com.android.launcher3.ItemInfo;
-import com.android.launcher3.ItemInfoWithIcon;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAnimUtils;
-import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.FirstFrameAnimatorHelper;
 import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.compat.LauncherAppsCompat;
-import com.android.launcher3.compat.ShortcutConfigActivityInfo;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.graphics.IconNormalizer;
-import com.android.launcher3.graphics.LauncherIcons;
-import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
-import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.Thunk;
-import com.android.launcher3.widget.PendingAddShortcutInfo;
 
 import java.util.Arrays;
-import java.util.List;
 
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_ICON_BADGED;
+import androidx.dynamicanimation.animation.FloatPropertyCompat;
+import androidx.dynamicanimation.animation.SpringAnimation;
+import androidx.dynamicanimation.animation.SpringForce;
 
-public class DragView extends View {
+public class DragView extends View implements LauncherStateManager.StateListener {
     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;
 
-    @Thunk static float sDragAlpha = 1f;
-
     private boolean mDrawBitmap = true;
     private Bitmap mBitmap;
     private Bitmap mCrossFadeBitmap;
@@ -97,6 +85,7 @@
     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;
@@ -137,6 +126,7 @@
         mLauncher = launcher;
         mDragLayer = launcher.getDragLayer();
         mDragController = launcher.getDragController();
+        mFirstFrameAnimatorHelper = new FirstFrameAnimatorHelper(this);
 
         final float scale = (bitmap.getWidth() + finalScaleDps) / bitmap.getWidth();
 
@@ -145,22 +135,14 @@
         setScaleY(initialScale);
 
         // Animate the view into the correct position
-        mAnim = LauncherAnimUtils.ofFloat(0f, 1f);
+        mAnim = ValueAnimator.ofFloat(0f, 1f);
         mAnim.setDuration(VIEW_ZOOM_DURATION);
-        mAnim.addUpdateListener(new AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                final float value = (Float) animation.getAnimatedValue();
-
-                setScaleX(initialScale + (value * (scale - initialScale)));
-                setScaleY(initialScale + (value * (scale - initialScale)));
-                if (sDragAlpha != 1f) {
-                    setAlpha(sDragAlpha * value + (1f - value));
-                }
-
-                if (getParent() == null) {
-                    animation.cancel();
-                }
+        mAnim.addUpdateListener(animation -> {
+            final float value = (Float) animation.getAnimatedValue();
+            setScaleX(initialScale + (value * (scale - initialScale)));
+            setScaleY(initialScale + (value * (scale - initialScale)));
+            if (!isAttachedToWindow()) {
+                animation.cancel();
             }
         });
 
@@ -192,13 +174,34 @@
         setElevation(getResources().getDimension(R.dimen.drag_elevation));
     }
 
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mLauncher.getStateManager().addStateListener(this);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        mLauncher.getStateManager().removeStateListener(this);
+    }
+
+    @Override
+    public void onStateTransitionStart(LauncherState toState) { }
+
+    @Override
+    public void onStateTransitionComplete(LauncherState finalState) {
+        setVisibility((finalState == LauncherState.NORMAL
+                || finalState == LauncherState.SPRING_LOADED) ? VISIBLE : INVISIBLE);
+    }
+
     /**
      * Initialize {@code #mIconDrawable} if the item can be represented using
      * an {@link AdaptiveIconDrawable} or {@link FolderAdaptiveIcon}.
      */
     @TargetApi(Build.VERSION_CODES.O)
     public void setItemInfo(final ItemInfo info) {
-        if (!(FeatureFlags.LAUNCHER3_SPRING_ICONS && Utilities.ATLEAST_OREO)) {
+        if (!Utilities.ATLEAST_OREO) {
             return;
         }
         if (info.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
@@ -207,17 +210,16 @@
             return;
         }
         // Load the adaptive icon on a background thread and add the view in ui thread.
-        final Looper workerLooper = LauncherModel.getWorkerLooper();
-        new Handler(workerLooper).postAtFrontOfQueue(new Runnable() {
+        new Handler(LauncherModel.getWorkerLooper()).postAtFrontOfQueue(new Runnable() {
             @Override
             public void run() {
-                LauncherAppState appState = LauncherAppState.getInstance(mLauncher);
                 Object[] outObj = new Object[1];
-                final Drawable dr = getFullDrawable(info, appState, outObj);
+                int w = mBitmap.getWidth();
+                int h = mBitmap.getHeight();
+                Drawable dr = Utilities.getFullDrawable(mLauncher, info, w, h,
+                        false /* flattenDrawable */, outObj);
 
                 if (dr instanceof AdaptiveIconDrawable) {
-                    int w = mBitmap.getWidth();
-                    int h = mBitmap.getHeight();
                     int blurMargin = (int) mLauncher.getResources()
                             .getDimension(R.dimen.blur_size_medium_outline) / 2;
 
@@ -226,13 +228,23 @@
                     // 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(info, appState, outObj[0]);
+                    mBadge = getBadge(mLauncher, info, outObj[0]);
                     mBadge.setBounds(badgeBounds);
 
-                    LauncherIcons li = LauncherIcons.obtain(mLauncher);
-                    Utilities.scaleRectAboutCenter(bounds,
-                            li.getNormalizer().getScale(dr, null, null, null));
-                    li.recycle();
+                    // Do not draw the background in case of folder as its translucent
+                    mDrawBitmap = !(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));
+                    }
                     AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) dr;
 
                     // Shrink very tiny bit so that the clip path is smaller than the original bitmap
@@ -268,9 +280,6 @@
                             // Assign the variable on the UI thread to avoid race conditions.
                             mScaledMaskPath = mask;
 
-                            // Do not draw the background in case of folder as its translucent
-                            mDrawBitmap = !(dr instanceof FolderAdaptiveIcon);
-
                             if (info.isDisabled()) {
                                 FastBitmapDrawable d = new FastBitmapDrawable((Bitmap) null);
                                 d.setIsDisabled(true);
@@ -315,83 +324,6 @@
         invalidate();
     }
 
-    /**
-     * Returns the full drawable for {@param info}.
-     * @param outObj this is set to the internal data associated with {@param info},
-     *               eg {@link LauncherActivityInfo} or {@link ShortcutInfoCompat}.
-     */
-    private Drawable getFullDrawable(ItemInfo info, LauncherAppState appState, Object[] outObj) {
-        if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
-            LauncherActivityInfo activityInfo = LauncherAppsCompat.getInstance(mLauncher)
-                    .resolveActivity(info.getIntent(), info.user);
-            outObj[0] = activityInfo;
-            return (activityInfo != null) ? appState.getIconCache()
-                    .getFullResIcon(activityInfo, false) : null;
-        } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
-            if (info instanceof PendingAddShortcutInfo) {
-                ShortcutConfigActivityInfo activityInfo =
-                        ((PendingAddShortcutInfo) info).activityInfo;
-                outObj[0] = activityInfo;
-                return activityInfo.getFullResIcon(appState.getIconCache());
-            }
-            ShortcutKey key = ShortcutKey.fromItemInfo(info);
-            DeepShortcutManager sm = DeepShortcutManager.getInstance(mLauncher);
-            List<ShortcutInfoCompat> si = sm.queryForFullDetails(
-                    key.componentName.getPackageName(), Arrays.asList(key.getId()), key.user);
-            if (si.isEmpty()) {
-                return null;
-            } else {
-                outObj[0] = si.get(0);
-                return sm.getShortcutIconDrawable(si.get(0),
-                        appState.getInvariantDeviceProfile().fillResIconDpi);
-            }
-        } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
-            FolderAdaptiveIcon icon =  FolderAdaptiveIcon.createFolderAdaptiveIcon(
-                    mLauncher, info.id, new Point(mBitmap.getWidth(), mBitmap.getHeight()));
-            if (icon == null) {
-                return null;
-            }
-            outObj[0] = icon;
-            return icon;
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * For apps icons and shortcut icons that have badges, this method creates a drawable that can
-     * later on be rendered on top of the layers for the badges. For app icons, work profile badges
-     * can only be applied. For deep shortcuts, when dragged from the pop up container, there's no
-     * badge. When dragged from workspace or folder, it may contain app AND/OR work profile badge
-     **/
-
-    @TargetApi(Build.VERSION_CODES.O)
-    private Drawable getBadge(ItemInfo info, LauncherAppState appState, Object obj) {
-        int iconSize = appState.getInvariantDeviceProfile().iconBitmapSize;
-        if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
-            boolean iconBadged = (info instanceof ItemInfoWithIcon)
-                    && (((ItemInfoWithIcon) info).runtimeStatusFlags & FLAG_ICON_BADGED) > 0;
-            if ((info.id == ItemInfo.NO_ID && !iconBadged)
-                    || !(obj instanceof ShortcutInfoCompat)) {
-                // The item is not yet added on home screen.
-                return new FixedSizeEmptyDrawable(iconSize);
-            }
-            ShortcutInfoCompat si = (ShortcutInfoCompat) obj;
-            LauncherIcons li = LauncherIcons.obtain(appState.getContext());
-            Bitmap badge = li.getShortcutInfoBadge(si, appState.getIconCache()).iconBitmap;
-            li.recycle();
-            float badgeSize = mLauncher.getResources().getDimension(R.dimen.profile_badge_size);
-            float insetFraction = (iconSize - badgeSize) / iconSize;
-            return new InsetDrawable(new FastBitmapDrawable(badge),
-                    insetFraction, insetFraction, 0, 0);
-        } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
-            return ((FolderAdaptiveIcon) obj).getBadge();
-        } else {
-            return mLauncher.getPackageManager()
-                    .getUserBadgedIcon(new FixedSizeEmptyDrawable(iconSize), info.user);
-        }
-    }
-
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         setMeasuredDimension(mBitmap.getWidth(), mBitmap.getHeight());
@@ -481,15 +413,12 @@
     }
 
     public void crossFade(int duration) {
-        ValueAnimator va = LauncherAnimUtils.ofFloat(0f, 1f);
+        ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
         va.setDuration(duration);
         va.setInterpolator(Interpolators.DEACCEL_1_5);
-        va.addUpdateListener(new AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                mCrossFadeProgress = animation.getAnimatedFraction();
-                invalidate();
-            }
+        va.addUpdateListener(a -> {
+            mCrossFadeProgress = a.getAnimatedFraction();
+            invalidate();
         });
         va.start();
     }
@@ -680,24 +609,4 @@
             mSpring.animateToFinalPosition(Utilities.boundToRange(value, -mDelta, mDelta));
         }
     }
-
-    private static class FixedSizeEmptyDrawable extends ColorDrawable {
-
-        private final int mSize;
-
-        public FixedSizeEmptyDrawable(int size) {
-            super(Color.TRANSPARENT);
-            mSize = size;
-        }
-
-        @Override
-        public int getIntrinsicHeight() {
-            return mSize;
-        }
-
-        @Override
-        public int getIntrinsicWidth() {
-            return mSize;
-        }
-    }
 }
diff --git a/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java b/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java
index e794744..589ad25 100644
--- a/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java
+++ b/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java
@@ -91,12 +91,13 @@
         return mDropTarget;
     }
 
-    public Runnable getFlingAnimation(DropTarget.DragObject dragObject) {
+    public Runnable getFlingAnimation(DropTarget.DragObject dragObject, DragOptions options) {
         PointF vel = isFlingingToDelete();
-        if (vel == null) {
+        options.isFlingToDelete = vel != null;
+        if (!options.isFlingToDelete) {
             return null;
         }
-        return new FlingAnimation(dragObject, vel, mDropTarget, mLauncher);
+        return new FlingAnimation(dragObject, vel, mDropTarget, mLauncher, options);
     }
 
     /**
diff --git a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
index 5576d91..0c5a1fc 100644
--- a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
+++ b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
@@ -19,11 +19,8 @@
 import android.annotation.TargetApi;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
-import android.graphics.ColorFilter;
 import android.graphics.Matrix;
-import android.graphics.Paint;
 import android.graphics.Path;
-import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.drawable.AdaptiveIconDrawable;
 import android.graphics.drawable.ColorDrawable;
@@ -36,7 +33,8 @@
 import com.android.launcher3.R;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.folder.PreviewBackground;
-import com.android.launcher3.graphics.BitmapRenderer;
+import com.android.launcher3.graphics.ShiftedBitmapDrawable;
+import com.android.launcher3.icons.BitmapRenderer;
 import com.android.launcher3.util.Preconditions;
 
 /**
@@ -48,11 +46,15 @@
 
     private final Drawable mBadge;
     private final Path mMask;
+    private final ConstantState mConstantState;
 
     private FolderAdaptiveIcon(Drawable bg, Drawable fg, Drawable badge, Path mask) {
         super(bg, fg);
         mBadge = badge;
         mMask = mask;
+
+        mConstantState = new MyConstantState(bg.getConstantState(), fg.getConstantState(),
+                badge.getConstantState(), mask);
     }
 
     @Override
@@ -65,7 +67,7 @@
     }
 
     public static FolderAdaptiveIcon createFolderAdaptiveIcon(
-            Launcher launcher, long folderId, Point dragViewSize) {
+            Launcher launcher, int folderId, Point dragViewSize) {
         Preconditions.assertNonUiThread();
         int margin = launcher.getResources()
                 .getDimensionPixelSize(R.dimen.blur_size_medium_outline);
@@ -102,7 +104,7 @@
         c.setBitmap(badgeBitmap);
         bg.drawShadow(c);
         bg.drawBackgroundStroke(c);
-        icon.drawBadge(c);
+        icon.drawDot(c);
 
         // Initialize preview
         final float sizeScaleFactor = 1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction();
@@ -134,38 +136,34 @@
         return new FolderAdaptiveIcon(new ColorDrawable(bg.getBgColor()), foreground, badge, mask);
     }
 
-    /**
-     * A simple drawable which draws a bitmap at a fixed position irrespective of the bounds
-     */
-    private static class ShiftedBitmapDrawable extends Drawable {
+    @Override
+    public ConstantState getConstantState() {
+        return mConstantState;
+    }
 
-        private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
-        private final Bitmap mBitmap;
-        private final float mShiftX;
-        private final float mShiftY;
+    private static class MyConstantState extends ConstantState {
+        private final ConstantState mBg;
+        private final ConstantState mFg;
+        private final ConstantState mBadge;
+        private final Path mMask;
 
-        ShiftedBitmapDrawable(Bitmap bitmap, float shiftX, float shiftY) {
-            mBitmap = bitmap;
-            mShiftX = shiftX;
-            mShiftY = shiftY;
+        MyConstantState(ConstantState bg, ConstantState fg, ConstantState badge, Path mask) {
+            mBg = bg;
+            mFg = fg;
+            mBadge = badge;
+            mMask = mask;
         }
 
         @Override
-        public void draw(Canvas canvas) {
-            canvas.drawBitmap(mBitmap, mShiftX, mShiftY, mPaint);
+        public Drawable newDrawable() {
+            return new FolderAdaptiveIcon(mBg.newDrawable(), mFg.newDrawable(),
+                    mBadge.newDrawable(), mMask);
         }
 
         @Override
-        public void setAlpha(int i) { }
-
-        @Override
-        public void setColorFilter(ColorFilter colorFilter) {
-            mPaint.setColorFilter(colorFilter);
-        }
-
-        @Override
-        public int getOpacity() {
-            return PixelFormat.TRANSLUCENT;
+        public int getChangingConfigurations() {
+            return mBg.getChangingConfigurations() & mFg.getChangingConfigurations()
+                    & mBadge.getChangingConfigurations();
         }
     }
 }
diff --git a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
index 52167bb..91a31aa 100644
--- a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
+++ b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
@@ -22,13 +22,15 @@
 import android.content.Context;
 import android.content.pm.LauncherApps;
 import android.content.pm.LauncherApps.PinItemRequest;
+import android.content.pm.PackageManager;
 import android.content.pm.ShortcutInfo;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.Process;
 
 import com.android.launcher3.FastBitmapDrawable;
-import com.android.launcher3.IconCache;
+import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
@@ -65,7 +67,7 @@
     }
 
     @Override
-    public CharSequence getLabel() {
+    public CharSequence getLabel(PackageManager pm) {
         return mInfo.getShortLabel();
     }
 
@@ -80,13 +82,13 @@
     }
 
     @Override
-    public com.android.launcher3.ShortcutInfo createShortcutInfo() {
+    public WorkspaceItemInfo createWorkspaceItemInfo() {
         // Total duration for the drop animation to complete.
         long duration = mContext.getResources().getInteger(R.integer.config_dropAnimMaxDuration) +
                 LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY +
                 LauncherAnimUtils.SPRING_LOADED_TRANSITION_MS;
         // Delay the actual accept() call until the drop animation is complete.
-        return LauncherAppsCompatVO.createShortcutInfoFromPinItemRequest(
+        return LauncherAppsCompatVO.createWorkspaceItemFromPinItemRequest(
                 mContext, mRequest, duration);
     }
 
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 6b13da7..2ef6d70 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -24,23 +24,22 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.annotation.SuppressLint;
+import android.appwidget.AppWidgetHostView;
 import android.content.Context;
 import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Path;
 import android.graphics.Rect;
 import android.text.InputType;
 import android.text.Selection;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Pair;
-import android.view.ActionMode;
 import android.view.FocusFinder;
 import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MenuItem;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewDebug;
-import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AnimationUtils;
 import android.view.inputmethod.EditorInfo;
@@ -63,9 +62,10 @@
 import com.android.launcher3.OnAlarmListener;
 import com.android.launcher3.PagedView;
 import com.android.launcher3.R;
-import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.Utilities;
+import com.android.launcher3.ShortcutAndWidgetContainer;
+import com.android.launcher3.Workspace;
 import com.android.launcher3.Workspace.ItemOperator;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dragndrop.DragController;
@@ -77,6 +77,7 @@
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.util.Thunk;
+import com.android.launcher3.views.ClipPathView;
 import com.android.launcher3.widget.PendingAddShortcutInfo;
 
 import java.util.ArrayList;
@@ -87,7 +88,7 @@
 /**
  * Represents a set of icons chosen by the user or generated by the system.
  */
-public class Folder extends AbstractFloatingView implements DragSource,
+public class Folder extends AbstractFloatingView implements ClipPathView, DragSource,
         View.OnLongClickListener, DropTarget, FolderListener, TextView.OnEditorActionListener,
         View.OnFocusChangeListener, DragListener, ExtendedEditText.OnBackKeyListener {
     private static final String TAG = "Launcher.Folder";
@@ -123,6 +124,7 @@
     private static final int REORDER_DELAY = 250;
     private static final int ON_EXIT_CLOSE_DELAY = 400;
     private static final Rect sTempRect = new Rect();
+    private static final int MIN_FOLDERS_FOR_HARDWARE_OPTIMIZATION = 10;
 
     private static String sDefaultFolderName;
     private static String sHintText;
@@ -152,6 +154,8 @@
     // Cell ranks used for drag and drop
     @Thunk int mTargetRank, mPrevTargetRank, mEmptyCellRank;
 
+    private Path mClipPath;
+
     @ViewDebug.ExportedProperty(category = "launcher",
             mapping = {
                     @ViewDebug.IntToString(from = STATE_NONE, to = "STATE_NONE"),
@@ -191,14 +195,9 @@
     public Folder(Context context, AttributeSet attrs) {
         super(context, attrs);
         setAlwaysDrawnWithCacheEnabled(false);
-        Resources res = getResources();
 
-        if (sDefaultFolderName == null) {
-            sDefaultFolderName = res.getString(R.string.folder_name);
-        }
-        if (sHintText == null) {
-            sHintText = res.getString(R.string.folder_hint_text);
-        }
+        setLocaleDependentFields(getResources(), false /* force */);
+
         mLauncher = Launcher.getLauncher(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
@@ -216,27 +215,6 @@
         mFolderName = findViewById(R.id.folder_name);
         mFolderName.setOnBackKeyListener(this);
         mFolderName.setOnFocusChangeListener(this);
-
-        if (!Utilities.ATLEAST_MARSHMALLOW) {
-            // We disable action mode in older OSes where floating selection menu is not yet
-            // available.
-            mFolderName.setCustomSelectionActionModeCallback(new ActionMode.Callback() {
-                public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
-                    return false;
-                }
-
-                public boolean onCreateActionMode(ActionMode mode, Menu menu) {
-                    return false;
-                }
-
-                public void onDestroyActionMode(ActionMode mode) {
-                }
-
-                public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
-                    return false;
-                }
-            });
-        }
         mFolderName.setOnEditorActionListener(this);
         mFolderName.setSelectAllOnFocus(true);
         mFolderName.setInputType(mFolderName.getInputType()
@@ -262,8 +240,8 @@
 
     public boolean startDrag(View v, DragOptions options) {
         Object tag = v.getTag();
-        if (tag instanceof ShortcutInfo) {
-            ShortcutInfo item = (ShortcutInfo) tag;
+        if (tag instanceof WorkspaceItemInfo) {
+            WorkspaceItemInfo item = (WorkspaceItemInfo) tag;
 
             mEmptyCellRank = item.rank;
             mCurrentDragView = v;
@@ -295,13 +273,13 @@
         }
 
         mContent.removeItem(mCurrentDragView);
-        if (dragObject.dragInfo instanceof ShortcutInfo) {
+        if (dragObject.dragInfo instanceof WorkspaceItemInfo) {
             mItemsInvalidated = true;
 
             // We do not want to get events for the item being removed, as they will get handled
             // when the drop completes
             try (SuppressInfoChanges s = new SuppressInfoChanges()) {
-                mInfo.remove((ShortcutInfo) dragObject.dragInfo, true);
+                mInfo.remove((WorkspaceItemInfo) dragObject.dragInfo, true);
             }
         }
         mDragInProgress = true;
@@ -405,7 +383,7 @@
 
     void bind(FolderInfo info) {
         mInfo = info;
-        ArrayList<ShortcutInfo> children = info.contents;
+        ArrayList<WorkspaceItemInfo> children = info.contents;
         Collections.sort(children, ITEM_POS_COMPARATOR);
         mContent.bindItems(children);
 
@@ -456,21 +434,44 @@
         if (mCurrentAnimator != null && mCurrentAnimator.isRunning()) {
             mCurrentAnimator.cancel();
         }
+        final Workspace workspace = mLauncher.getWorkspace();
+        final CellLayout currentCellLayout =
+                (CellLayout) workspace.getChildAt(workspace.getCurrentPage());
+        final boolean useHardware = shouldUseHardwareLayerForAnimation(currentCellLayout);
+        final boolean wasHardwareAccelerated = currentCellLayout.isHardwareLayerEnabled();
+
         a.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
+                if (useHardware) {
+                    currentCellLayout.enableHardwareLayer(true);
+                }
                 mState = STATE_ANIMATING;
                 mCurrentAnimator = a;
             }
 
             @Override
             public void onAnimationEnd(Animator animation) {
+                if (useHardware) {
+                    currentCellLayout.enableHardwareLayer(wasHardwareAccelerated);
+                }
                 mCurrentAnimator = null;
             }
         });
         a.start();
     }
 
+    private boolean shouldUseHardwareLayerForAnimation(CellLayout currentCellLayout) {
+        int folderCount = 0;
+        final ShortcutAndWidgetContainer container = currentCellLayout.getShortcutsAndWidgets();
+        for (int i = container.getChildCount() - 1; i >= 0; --i) {
+            final View child = container.getChildAt(i);
+            if (child instanceof AppWidgetHostView) return false;
+            if (child instanceof FolderIcon) ++folderCount;
+        }
+        return folderCount >= MIN_FOLDERS_FOR_HARDWARE_OPTIMIZATION;
+    }
+
     /**
      * 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
@@ -592,6 +593,10 @@
     protected void handleClose(boolean animate) {
         mIsOpen = false;
 
+        if (!animate && mCurrentAnimator != null && mCurrentAnimator.isRunning()) {
+            mCurrentAnimator.cancel();
+        }
+
         if (isEditingName()) {
             mFolderName.dispatchBackKey();
         }
@@ -644,11 +649,10 @@
             mFolderIcon.setBackgroundVisible(true);
             mFolderIcon.mFolderName.setTextVisibility(true);
             if (wasAnimated) {
-                mFolderIcon.mBackground.fadeInBackgroundShadow();
-                mFolderIcon.mBackground.animateBackgroundStroke();
+                mFolderIcon.animateBgShadowAndStroke();
                 mFolderIcon.onFolderClose(mContent.getCurrentPage());
-                if (mFolderIcon.hasBadge()) {
-                    mFolderIcon.createBadgeScaleAnimator(0f, 1f).start();
+                if (mFolderIcon.hasDot()) {
+                    mFolderIcon.animateDotScale(0f, 1f);
                 }
                 mFolderIcon.requestFocus();
             }
@@ -827,7 +831,7 @@
             }
         } else {
             // The drag failed, we need to return the item to the folder
-            ShortcutInfo info = (ShortcutInfo) d.dragInfo;
+            WorkspaceItemInfo info = (WorkspaceItemInfo) d.dragInfo;
             View icon = (mCurrentDragView != null && mCurrentDragView.getTag() == info)
                     ? mCurrentDragView : mContent.createNewView(info);
             ArrayList<View> views = getItemsInReadingOrder();
@@ -896,7 +900,7 @@
         DeviceProfile grid = mLauncher.getDeviceProfile();
 
         DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
-        DragLayer parent = (DragLayer) mLauncher.findViewById(R.id.drag_layer);
+        DragLayer parent = mLauncher.getDragLayer();
         int width = getFolderWidth();
         int height = getFolderHeight();
 
@@ -908,8 +912,7 @@
 
         // We need to bound the folder to the currently visible workspace area
         if (mLauncher.getStateManager().getState().overviewUi) {
-            mLauncher.getDragLayer().getDescendantRectRelativeToSelf(mLauncher.getOverviewPanel(),
-                    sTempRect);
+            parent.getDescendantRectRelativeToSelf(mLauncher.getOverviewPanel(), sTempRect);
         } else {
             mLauncher.getWorkspace().getPageAreaRelativeToDragLayer(sTempRect);
         }
@@ -1050,7 +1053,7 @@
                         // folder
                         CellLayout cellLayout = mLauncher.getCellLayout(mInfo.container,
                                 mInfo.screenId);
-                        ShortcutInfo finalItem = mInfo.contents.remove(0);
+                        WorkspaceItemInfo finalItem = mInfo.contents.remove(0);
                         newIcon = mLauncher.createShortcut(cellLayout, finalItem);
                         mLauncher.getModelWriter().addOrMoveItemInDatabase(finalItem,
                                 mInfo.container, mInfo.screenId, mInfo.cellX, mInfo.cellY);
@@ -1138,9 +1141,9 @@
 
         PendingAddShortcutInfo pasi = d.dragInfo instanceof PendingAddShortcutInfo
                 ? (PendingAddShortcutInfo) d.dragInfo : null;
-        ShortcutInfo pasiSi = pasi != null ? pasi.activityInfo.createShortcutInfo() : null;
+        WorkspaceItemInfo pasiSi = pasi != null ? pasi.activityInfo.createWorkspaceItemInfo() : null;
         if (pasi != null && pasiSi == null) {
-            // There is no ShortcutInfo, so we have to go through a configuration activity.
+            // There is no WorkspaceItemInfo, so we have to go through a configuration activity.
             pasi.container = mInfo.id;
             pasi.rank = mEmptyCellRank;
 
@@ -1149,15 +1152,15 @@
             d.deferDragViewCleanupPostAnimation = false;
             mRearrangeOnClose = true;
         } else {
-            final ShortcutInfo si;
+            final WorkspaceItemInfo si;
             if (pasiSi != null) {
                 si = pasiSi;
             } else if (d.dragInfo instanceof AppInfo) {
                 // Came from all apps -- make a copy.
-                si = ((AppInfo) d.dragInfo).makeShortcut();
+                si = ((AppInfo) d.dragInfo).makeWorkspaceItem();
             } else {
-                // ShortcutInfo
-                si = (ShortcutInfo) d.dragInfo;
+                // WorkspaceItemInfo
+                si = (WorkspaceItemInfo) d.dragInfo;
             }
 
             View currentDragView;
@@ -1165,7 +1168,7 @@
                 currentDragView = mContent.createAndAddViewForRank(si, mEmptyCellRank);
 
                 // Actually move the item in the database if it was an external drag. Call this
-                // before creating the view, so that ShortcutInfo is updated appropriately.
+                // before creating the view, so that WorkspaceItemInfo is updated appropriately.
                 mLauncher.getModelWriter().addOrMoveItemInDatabase(
                         si, mInfo.id, 0, si.cellX, si.cellY);
 
@@ -1221,17 +1224,17 @@
     // This is used so the item doesn't immediately appear in the folder when added. In one case
     // we need to create the illusion that the item isn't added back to the folder yet, to
     // to correspond to the animation of the icon back into the folder. This is
-    public void hideItem(ShortcutInfo info) {
+    public void hideItem(WorkspaceItemInfo info) {
         View v = getViewForInfo(info);
         v.setVisibility(INVISIBLE);
     }
-    public void showItem(ShortcutInfo info) {
+    public void showItem(WorkspaceItemInfo info) {
         View v = getViewForInfo(info);
         v.setVisibility(VISIBLE);
     }
 
     @Override
-    public void onAdd(ShortcutInfo item, int rank) {
+    public void onAdd(WorkspaceItemInfo item, int rank) {
         View view = mContent.createAndAddViewForRank(item, rank);
         mLauncher.getModelWriter().addOrMoveItemInDatabase(item, mInfo.id, 0, item.cellX,
                 item.cellY);
@@ -1242,7 +1245,7 @@
         mItemsInvalidated = true;
     }
 
-    public void onRemove(ShortcutInfo item) {
+    public void onRemove(WorkspaceItemInfo item) {
         mItemsInvalidated = true;
         View v = getViewForInfo(item);
         mContent.removeItem(v);
@@ -1260,7 +1263,7 @@
         }
     }
 
-    private View getViewForInfo(final ShortcutInfo item) {
+    private View getViewForInfo(final WorkspaceItemInfo item) {
         return mContent.iterateOverItems(new ItemOperator() {
 
             @Override
@@ -1433,7 +1436,12 @@
     @Override
     public void logActionCommand(int command) {
         mLauncher.getUserEventDispatcher().logActionCommand(
-                command, getFolderIcon(), ContainerType.FOLDER);
+                command, getFolderIcon(), getLogContainerType());
+    }
+
+    @Override
+    public int getLogContainerType() {
+        return ContainerType.FOLDER;
     }
 
     @Override
@@ -1473,4 +1481,35 @@
         }
         return false;
     }
+
+    public static void setLocaleDependentFields(Resources res, boolean force) {
+        if (sDefaultFolderName == null || force) {
+            sDefaultFolderName = res.getString(R.string.folder_name);
+        }
+        if (sHintText == null || force) {
+            sHintText = res.getString(R.string.folder_hint_text);
+        }
+    }
+
+    /**
+     * Alternative to using {@link #getClipToOutline()} as it only works with derivatives of
+     * rounded rect.
+     */
+    @Override
+    public void setClipPath(Path clipPath) {
+        mClipPath = clipPath;
+        invalidate();
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        if (mClipPath != null) {
+            int count = canvas.save();
+            canvas.clipPath(mClipPath);
+            super.draw(canvas);
+            canvas.restoreToCount(count);
+        } else {
+            super.draw(canvas);
+        }
+    }
 }
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index 9ae3775..962f215 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -19,6 +19,8 @@
 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;
+import static com.android.launcher3.graphics.IconShape.getShape;
+import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -27,23 +29,22 @@
 import android.animation.TimeInterpolator;
 import android.content.Context;
 import android.content.res.Resources;
-import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.drawable.GradientDrawable;
-import android.support.v4.graphics.ColorUtils;
 import android.util.Property;
 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.LauncherAnimUtils;
 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.anim.RoundedRectRevealOutlineProvider;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.util.Themes;
 
@@ -153,9 +154,11 @@
         final float yDistance = initialY - lp.y;
 
         // Set up the Folder background.
-        final int finalColor = Themes.getAttrColor(mContext, android.R.attr.colorPrimary);
-        final int initialColor =
-                ColorUtils.setAlphaComponent(finalColor, mPreviewBackground.getBackgroundAlpha());
+        final int finalColor = ColorUtils.setAlphaComponent(
+                Themes.getAttrColor(mContext, R.attr.folderFillColor), 255);
+        final int initialColor = setColorAlphaBound(
+                finalColor, mPreviewBackground.getBackgroundAlpha());
+        mFolderBackground.mutate();
         mFolderBackground.setColor(mIsOpening ? initialColor : finalColor);
 
         // Set up the reveal animation that clips the Folder.
@@ -166,11 +169,10 @@
                 Math.round((totalOffsetX + initialSize) / initialScale),
                 Math.round((paddingOffsetY + initialSize) / initialScale));
         Rect endRect = new Rect(0, 0, lp.width, lp.height);
-        float initialRadius = initialSize / initialScale / 2f;
-        float finalRadius = Utilities.pxFromDp(2, mContext.getResources().getDisplayMetrics());
+        float finalRadius = ResourceUtils.pxFromDp(2, mContext.getResources().getDisplayMetrics());
 
         // Create the animators.
-        AnimatorSet a = LauncherAnimUtils.createAnimatorSet();
+        AnimatorSet a = new AnimatorSet();
 
         // Initialize the Folder items' text.
         PropertyResetListener colorResetListener =
@@ -189,14 +191,8 @@
         play(a, getAnimator(mFolder, SCALE_PROPERTY, initialScale, finalScale));
         play(a, getAnimator(mFolderBackground, "color", initialColor, finalColor));
         play(a, mFolderIcon.mFolderName.createTextAlphaAnimator(!mIsOpening));
-        RoundedRectRevealOutlineProvider outlineProvider = new RoundedRectRevealOutlineProvider(
-                initialRadius, finalRadius, startRect, endRect) {
-            @Override
-            public boolean shouldRemoveElevationDuringAnimation() {
-                return true;
-            }
-        };
-        play(a, outlineProvider.createRevealAnimator(mFolder, !mIsOpening));
+        play(a, getShape().createRevealAnimator(
+                mFolder, startRect, endRect, finalRadius, !mIsOpening));
 
         // Animate the elevation midway so that the shadow is not noticeable in the background.
         int midDuration = mDuration / 2;
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index cb5d872..250169c 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -20,24 +20,24 @@
 import static com.android.launcher3.folder.PreviewItemManager.INITIAL_ITEM_ANIMATION_DURATION;
 
 import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.graphics.Canvas;
-import android.graphics.Point;
 import android.graphics.Rect;
-import android.graphics.Region;
 import android.graphics.drawable.Drawable;
-import android.os.Parcelable;
-import android.support.annotation.NonNull;
 import android.util.AttributeSet;
 import android.util.Property;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
+import android.view.ViewDebug;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
+import androidx.annotation.NonNull;
+
 import com.android.launcher3.Alarm;
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.BubbleTextView;
@@ -52,17 +52,17 @@
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.OnAlarmListener;
 import com.android.launcher3.R;
-import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.SimpleOnStylusPressListener;
 import com.android.launcher3.StylusEventHelper;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.badge.BadgeRenderer;
-import com.android.launcher3.badge.FolderBadgeInfo;
+import com.android.launcher3.dot.FolderDotInfo;
 import com.android.launcher3.dragndrop.BaseItemDragListener;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragView;
+import com.android.launcher3.icons.DotRenderer;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.widget.PendingAddShortcutInfo;
@@ -74,10 +74,10 @@
  * An icon that can appear on in the workspace representing an {@link Folder}.
  */
 public class FolderIcon extends FrameLayout implements FolderListener {
+
     @Thunk Launcher mLauncher;
     @Thunk Folder mFolder;
     private FolderInfo mInfo;
-    @Thunk static boolean sStaticValuesDirty = true;
 
     private CheckLongPressHelper mLongPressHelper;
     private StylusEventHelper mStylusEventHelper;
@@ -102,27 +102,29 @@
     private List<BubbleTextView> mCurrentPreviewItems = new ArrayList<>();
 
     boolean mAnimating = false;
-    private Rect mTempBounds = new Rect();
 
     private float mSlop;
 
     private Alarm mOpenAlarm = new Alarm();
 
-    private FolderBadgeInfo mBadgeInfo = new FolderBadgeInfo();
-    private BadgeRenderer mBadgeRenderer;
-    private float mBadgeScale;
-    private Point mTempSpaceForBadgeOffset = new Point();
+    @ViewDebug.ExportedProperty(category = "launcher", deepExport = true)
+    private FolderDotInfo mDotInfo = new FolderDotInfo();
+    private DotRenderer mDotRenderer;
+    @ViewDebug.ExportedProperty(category = "launcher", deepExport = true)
+    private DotRenderer.DrawParams mDotParams;
+    private float mDotScale;
+    private Animator mDotScaleAnim;
 
-    private static final Property<FolderIcon, Float> BADGE_SCALE_PROPERTY
-            = new Property<FolderIcon, Float>(Float.TYPE, "badgeScale") {
+    private static final Property<FolderIcon, Float> DOT_SCALE_PROPERTY
+            = new Property<FolderIcon, Float>(Float.TYPE, "dotScale") {
         @Override
         public Float get(FolderIcon folderIcon) {
-            return folderIcon.mBadgeScale;
+            return folderIcon.mDotScale;
         }
 
         @Override
         public void set(FolderIcon folderIcon, Float value) {
-            folderIcon.mBadgeScale = value;
+            folderIcon.mDotScale = value;
             folderIcon.invalidate();
         }
     };
@@ -143,6 +145,7 @@
         mPreviewLayoutRule = new ClippedFolderIconLayoutRule();
         mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
         mPreviewItemManager = new PreviewItemManager(this);
+        mDotParams = new DotRenderer.DrawParams();
     }
 
     public static FolderIcon fromXml(int resId, Launcher launcher, ViewGroup group,
@@ -155,7 +158,7 @@
                     "is dependent on this");
         }
 
-        DeviceProfile grid = launcher.getDeviceProfile();
+        DeviceProfile grid = launcher.getWallpaperDeviceProfile();
         FolderIcon icon = (FolderIcon) LayoutInflater.from(group.getContext())
                 .inflate(resId, group, false);
 
@@ -170,7 +173,7 @@
         icon.setOnClickListener(ItemClickHandler.INSTANCE);
         icon.mInfo = folderInfo;
         icon.mLauncher = launcher;
-        icon.mBadgeRenderer = launcher.getDeviceProfile().mBadgeRenderer;
+        icon.mDotRenderer = grid.mDotRenderer;
         icon.setContentDescription(launcher.getString(R.string.folder_name_format, folderInfo.title));
         Folder folder = Folder.fromXml(launcher);
         folder.setDragController(launcher.getDragController());
@@ -185,10 +188,22 @@
         return icon;
     }
 
-    @Override
-    protected Parcelable onSaveInstanceState() {
-        sStaticValuesDirty = true;
-        return super.onSaveInstanceState();
+    public void animateBgShadowAndStroke() {
+        mBackground.fadeInBackgroundShadow();
+        mBackground.animateBackgroundStroke();
+    }
+
+    public BubbleTextView getFolderName() {
+        return mFolderName;
+    }
+
+    public void getPreviewBounds(Rect outBounds) {
+        mPreviewItemManager.recomputePreviewDrawingParams();
+        mBackground.getBounds(outBounds);
+    }
+
+    public float getBackgroundStrokeWidth() {
+        return mBackground.getStrokeWidth();
     }
 
     public Folder getFolder() {
@@ -198,6 +213,7 @@
     private void setFolder(Folder folder) {
         mFolder = folder;
         mPreviewVerifier = new FolderIconPreviewVerifier(mLauncher.getDeviceProfile().inv);
+        mPreviewVerifier.setFolderInfo(mFolder.getInfo());
         updatePreviewItems(false);
     }
 
@@ -213,15 +229,15 @@
         return !mFolder.isDestroyed() && willAcceptItem(dragInfo);
     }
 
-    public void addItem(ShortcutInfo item) {
+    public void addItem(WorkspaceItemInfo item) {
         addItem(item, true);
     }
 
-    public void addItem(ShortcutInfo item, boolean animate) {
+    public void addItem(WorkspaceItemInfo item, boolean animate) {
         mInfo.add(item, animate);
     }
 
-    public void removeItem(ShortcutInfo item, boolean animate) {
+    public void removeItem(WorkspaceItemInfo item, boolean animate) {
         mInfo.remove(item, animate);
     }
 
@@ -234,7 +250,7 @@
         mOpenAlarm.setOnAlarmListener(mOnOpenListener);
         if (SPRING_LOADING_ENABLED &&
                 ((dragInfo instanceof AppInfo)
-                        || (dragInfo instanceof ShortcutInfo)
+                        || (dragInfo instanceof WorkspaceItemInfo)
                         || (dragInfo instanceof PendingAddShortcutInfo))) {
             mOpenAlarm.setAlarm(ON_OPEN_DELAY);
         }
@@ -251,8 +267,8 @@
         return mPreviewItemManager.prepareCreateAnimation(destView);
     }
 
-    public void performCreateAnimation(final ShortcutInfo destInfo, final View destView,
-            final ShortcutInfo srcInfo, final DragView srcView, Rect dstRect,
+    public void performCreateAnimation(final WorkspaceItemInfo destInfo, final View destView,
+            final WorkspaceItemInfo srcInfo, final DragView srcView, Rect dstRect,
             float scaleRelativeToDragLayer) {
         prepareCreateAnimation(destView);
         addItem(destInfo);
@@ -277,7 +293,7 @@
         mOpenAlarm.cancelAlarm();
     }
 
-    private void onDrop(final ShortcutInfo item, DragView animateView, Rect finalRect,
+    private void onDrop(final WorkspaceItemInfo item, DragView animateView, Rect finalRect,
             float scaleRelativeToDragLayer, int index,
             boolean itemReturnedOnFailedDrop) {
         item.cellX = -1;
@@ -369,24 +385,24 @@
     }
 
     public void onDrop(DragObject d, boolean itemReturnedOnFailedDrop) {
-        ShortcutInfo item;
+        WorkspaceItemInfo item;
         if (d.dragInfo instanceof AppInfo) {
             // Came from all apps -- make a copy
-            item = ((AppInfo) d.dragInfo).makeShortcut();
+            item = ((AppInfo) d.dragInfo).makeWorkspaceItem();
         } else if (d.dragSource instanceof BaseItemDragListener){
             // Came from a different window -- make a copy
-            item = new ShortcutInfo((ShortcutInfo) d.dragInfo);
+            item = new WorkspaceItemInfo((WorkspaceItemInfo) d.dragInfo);
         } else {
-            item = (ShortcutInfo) d.dragInfo;
+            item = (WorkspaceItemInfo) d.dragInfo;
         }
         mFolder.notifyDrop();
         onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(),
                 itemReturnedOnFailedDrop);
     }
 
-    public void setBadgeInfo(FolderBadgeInfo badgeInfo) {
-        updateBadgeScale(mBadgeInfo.hasBadge(), badgeInfo.hasBadge());
-        mBadgeInfo = badgeInfo;
+    public void setDotInfo(FolderDotInfo dotInfo) {
+        updateDotScale(mDotInfo.hasDot(), dotInfo.hasDot());
+        mDotInfo = dotInfo;
     }
 
     public ClippedFolderIconLayoutRule getLayoutRule() {
@@ -394,26 +410,41 @@
     }
 
     /**
-     * Sets mBadgeScale to 1 or 0, animating if wasBadged or isBadged is false
-     * (the badge is being added or removed).
+     * Sets mDotScale to 1 or 0, animating if wasDotted or isDotted is false
+     * (the dot is being added or removed).
      */
-    private void updateBadgeScale(boolean wasBadged, boolean isBadged) {
-        float newBadgeScale = isBadged ? 1f : 0f;
-        // Animate when a badge is first added or when it is removed.
-        if ((wasBadged ^ isBadged) && isShown()) {
-            createBadgeScaleAnimator(newBadgeScale).start();
+    private void updateDotScale(boolean wasDotted, boolean isDotted) {
+        float newDotScale = isDotted ? 1f : 0f;
+        // Animate when a dot is first added or when it is removed.
+        if ((wasDotted ^ isDotted) && isShown()) {
+            animateDotScale(newDotScale);
         } else {
-            mBadgeScale = newBadgeScale;
+            cancelDotScaleAnim();
+            mDotScale = newDotScale;
             invalidate();
         }
     }
 
-    public Animator createBadgeScaleAnimator(float... badgeScales) {
-        return ObjectAnimator.ofFloat(this, BADGE_SCALE_PROPERTY, badgeScales);
+    private void cancelDotScaleAnim() {
+        if (mDotScaleAnim != null) {
+            mDotScaleAnim.cancel();
+        }
     }
 
-    public boolean hasBadge() {
-        return mBadgeInfo != null && mBadgeInfo.hasBadge();
+    public void animateDotScale(float... dotScales) {
+        cancelDotScaleAnim();
+        mDotScaleAnim = ObjectAnimator.ofFloat(this, DOT_SCALE_PROPERTY, dotScales);
+        mDotScaleAnim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mDotScaleAnim = null;
+            }
+        });
+        mDotScaleAnim.start();
+    }
+
+    public boolean hasDot() {
+        return mDotInfo != null && mDotInfo.hasDot();
     }
 
     private float getLocalCenterForIndex(int index, int curNumItems, int[] center) {
@@ -465,41 +496,30 @@
         if (mFolder == null) return;
         if (mFolder.getItemCount() == 0 && !mAnimating) return;
 
-        final int saveCount;
-
-        if (canvas.isHardwareAccelerated()) {
-            saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null);
-        } else {
-            saveCount = canvas.save();
-            canvas.clipPath(mBackground.getClipPath());
-        }
-
+        final int saveCount = canvas.save();
+        canvas.clipPath(mBackground.getClipPath());
         mPreviewItemManager.draw(canvas);
-
-        if (canvas.isHardwareAccelerated()) {
-            mBackground.clipCanvasHardware(canvas);
-        }
         canvas.restoreToCount(saveCount);
 
         if (!mBackground.drawingDelegated()) {
             mBackground.drawBackgroundStroke(canvas);
         }
 
-        drawBadge(canvas);
+        drawDot(canvas);
     }
 
-    public void drawBadge(Canvas canvas) {
-        if ((mBadgeInfo != null && mBadgeInfo.hasBadge()) || mBadgeScale > 0) {
-            int offsetX = mBackground.getOffsetX();
-            int offsetY = mBackground.getOffsetY();
-            int previewSize = (int) (mBackground.previewSize * mBackground.mScale);
-            mTempBounds.set(offsetX, offsetY, offsetX + previewSize, offsetY + previewSize);
+    public void drawDot(Canvas canvas) {
+        if ((mDotInfo != null && mDotInfo.hasDot()) || mDotScale > 0) {
+            Rect iconBounds = mDotParams.iconBounds;
+            BubbleTextView.getIconBounds(this, iconBounds,
+                    mLauncher.getWallpaperDeviceProfile().iconSizePx);
+            float iconScale = (float) mBackground.previewSize / iconBounds.width();
+            Utilities.scaleRectAboutCenter(iconBounds, iconScale);
 
-            // If we are animating to the accepting state, animate the badge out.
-            float badgeScale = Math.max(0, mBadgeScale - mBackground.getScaleProgress());
-            mTempSpaceForBadgeOffset.set(getWidth() - mTempBounds.right, mTempBounds.top);
-            mBadgeRenderer.draw(canvas, mBackground.getBadgeColor(), mTempBounds,
-                    badgeScale, mTempSpaceForBadgeOffset);
+            // If we are animating to the accepting state, animate the dot out.
+            mDotParams.scale = Math.max(0, mDotScale - mBackground.getScaleProgress());
+            mDotParams.color = mBackground.getDotColor();
+            mDotRenderer.draw(canvas, mDotParams);
         }
     }
 
@@ -566,21 +586,21 @@
     }
 
     @Override
-    public void onAdd(ShortcutInfo item, int rank) {
-        boolean wasBadged = mBadgeInfo.hasBadge();
-        mBadgeInfo.addBadgeInfo(mLauncher.getBadgeInfoForItem(item));
-        boolean isBadged = mBadgeInfo.hasBadge();
-        updateBadgeScale(wasBadged, isBadged);
+    public void onAdd(WorkspaceItemInfo item, int rank) {
+        boolean wasDotted = mDotInfo.hasDot();
+        mDotInfo.addDotInfo(mLauncher.getDotInfoForItem(item));
+        boolean isDotted = mDotInfo.hasDot();
+        updateDotScale(wasDotted, isDotted);
         invalidate();
         requestLayout();
     }
 
     @Override
-    public void onRemove(ShortcutInfo item) {
-        boolean wasBadged = mBadgeInfo.hasBadge();
-        mBadgeInfo.subtractBadgeInfo(mLauncher.getBadgeInfoForItem(item));
-        boolean isBadged = mBadgeInfo.hasBadge();
-        updateBadgeScale(wasBadged, isBadged);
+    public void onRemove(WorkspaceItemInfo item) {
+        boolean wasDotted = mDotInfo.hasDot();
+        mDotInfo.subtractDotInfo(mLauncher.getDotInfoForItem(item));
+        boolean isDotted = mDotInfo.hasDot();
+        updateDotScale(wasDotted, isDotted);
         invalidate();
         requestLayout();
     }
diff --git a/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java b/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java
index 5a27cd4..4c84e35 100644
--- a/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java
+++ b/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.folder;
 
+import android.util.Log;
+
 import com.android.launcher3.FolderInfo;
 import com.android.launcher3.InvariantDeviceProfile;
 
@@ -26,14 +28,20 @@
  */
 public class FolderIconPreviewVerifier {
 
+    private static final String TAG = "FolderPreviewVerifier";
+
     private final int mMaxGridCountX;
     private final int mMaxGridCountY;
     private final int mMaxItemsPerPage;
-    private final int[] mGridSize = new int[2];
+    private final int[] mGridSize = new int[] { 1, 1 };
 
+    private int mNumItemsInFolder;
     private int mGridCountX;
     private boolean mDisplayingUpperLeftQuadrant = false;
 
+    /**
+     * Note: must call {@link #setFolderInfo(FolderInfo)} manually for verifier to work.
+     */
     public FolderIconPreviewVerifier(InvariantDeviceProfile profile) {
         mMaxGridCountX = profile.numFolderColumns;
         mMaxGridCountY = profile.numFolderRows;
@@ -42,11 +50,14 @@
 
     public void setFolderInfo(FolderInfo info) {
         int numItemsInFolder = info.contents.size();
-        FolderPagedView.calculateGridSize(numItemsInFolder, 0, 0, mMaxGridCountX,
-                mMaxGridCountY, mMaxItemsPerPage, mGridSize);
-        mGridCountX = mGridSize[0];
+        if (numItemsInFolder != mNumItemsInFolder) {
+            FolderPagedView.calculateGridSize(numItemsInFolder, 0, 0, mMaxGridCountX,
+                    mMaxGridCountY, mMaxItemsPerPage, mGridSize);
+            mGridCountX = mGridSize[0];
 
-        mDisplayingUpperLeftQuadrant = numItemsInFolder > MAX_NUM_ITEMS_IN_PREVIEW;
+            mDisplayingUpperLeftQuadrant = numItemsInFolder > MAX_NUM_ITEMS_IN_PREVIEW;
+            mNumItemsInFolder = numItemsInFolder;
+        }
     }
 
     /**
@@ -62,6 +73,10 @@
      * @return True iff the icon is in the 2x2 upper left quadrant of the Folder.
      */
     public boolean isItemInPreview(int page, int rank) {
+        if (mGridSize[0] == 1) {
+            Log.w(TAG, "setFolderInfo not called before checking if item is in preview.");
+        }
+
         // First page items are laid out such that the first 4 items are always in the upper
         // left quadrant. For all other pages, we need to check the row and col.
         if (page > 0 || mDisplayingUpperLeftQuadrant) {
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 9be71f9..57105e7 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -38,7 +38,7 @@
 import com.android.launcher3.PagedView;
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutAndWidgetContainer;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace.ItemOperator;
 import com.android.launcher3.anim.Interpolators;
@@ -178,9 +178,9 @@
     /**
      * Binds items to the layout.
      */
-    public void bindItems(ArrayList<ShortcutInfo> items) {
+    public void bindItems(ArrayList<WorkspaceItemInfo> items) {
         ArrayList<View> icons = new ArrayList<>();
-        for (ShortcutInfo item : items) {
+        for (WorkspaceItemInfo item : items) {
             icons.add(createNewView(item));
         }
         arrangeChildren(icons, icons.size(), false);
@@ -203,7 +203,7 @@
         return rank;
     }
 
-    public View createAndAddViewForRank(ShortcutInfo item, int rank) {
+    public View createAndAddViewForRank(WorkspaceItemInfo item, int rank) {
         View icon = createNewView(item);
         allocateSpaceForRank(rank);
         addViewForRank(icon, item, rank);
@@ -214,7 +214,7 @@
      * Adds the {@param view} to the layout based on {@param rank} and updated the position
      * related attributes. It assumes that {@param item} is already attached to the view.
      */
-    public void addViewForRank(View view, ShortcutInfo item, int rank) {
+    public void addViewForRank(View view, WorkspaceItemInfo item, int rank) {
         int pagePos = rank % mMaxItemsPerPage;
         int pageNo = rank / mMaxItemsPerPage;
 
@@ -225,15 +225,14 @@
         CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
         lp.cellX = item.cellX;
         lp.cellY = item.cellY;
-        getPageAt(pageNo).addViewToCellLayout(
-                view, -1, mFolder.mLauncher.getViewIdForItem(item), lp, true);
+        getPageAt(pageNo).addViewToCellLayout(view, -1, item.getViewId(), lp, true);
     }
 
     @SuppressLint("InflateParams")
-    public View createNewView(ShortcutInfo item) {
+    public View createNewView(WorkspaceItemInfo item) {
         final BubbleTextView textView = (BubbleTextView) mInflater.inflate(
                 R.layout.folder_application, null, false);
-        textView.applyFromShortcutInfo(item);
+        textView.applyFromWorkspaceItem(item);
         textView.setHapticFeedbackEnabled(false);
         textView.setOnClickListener(ItemClickHandler.INSTANCE);
         textView.setOnLongClickListener(mFolder);
@@ -322,6 +321,7 @@
 
         FolderIconPreviewVerifier verifier = new FolderIconPreviewVerifier(
                 Launcher.getLauncher(getContext()).getDeviceProfile().inv);
+        verifier.setFolderInfo(mFolder.getInfo());
         rank = 0;
         for (int i = 0; i < itemCount; i++) {
             View v = list.size() > i ? list.get(i) : null;
@@ -351,8 +351,7 @@
                 }
                 lp.cellX = info.cellX;
                 lp.cellY = info.cellY;
-                currentPage.addViewToCellLayout(
-                        v, -1, mFolder.mLauncher.getViewIdForItem(info), lp, true);
+                currentPage.addViewToCellLayout(v, -1, info.getViewId(), lp, true);
 
                 if (verifier.isItemInPreview(rank) && v instanceof BubbleTextView) {
                     ((BubbleTextView) v).verifyHighRes();
@@ -493,7 +492,7 @@
         int delta = scroll - getScrollX();
         if (delta != 0) {
             mScroller.setInterpolator(Interpolators.DEACCEL);
-            mScroller.startScroll(getScrollX(), 0, delta, 0, Folder.SCROLL_HINT_DURATION);
+            mScroller.startScroll(getScrollX(), delta, Folder.SCROLL_HINT_DURATION);
             invalidate();
         }
     }
@@ -632,7 +631,7 @@
             if (v != null) {
                 if (pageToAnimate != p) {
                     page.removeView(v);
-                    addViewForRank(v, (ShortcutInfo) v.getTag(), moveStart);
+                    addViewForRank(v, (WorkspaceItemInfo) v.getTag(), moveStart);
                 } else {
                     // Do a fake animation before removing it.
                     final int newRank = moveStart;
@@ -645,7 +644,7 @@
                             mPendingAnimations.remove(v);
                             v.setTranslationX(oldTranslateX);
                             ((CellLayout) v.getParent().getParent()).removeView(v);
-                            addViewForRank(v, (ShortcutInfo) v.getTag(), newRank);
+                            addViewForRank(v, (WorkspaceItemInfo) v.getTag(), newRank);
                         }
                     };
                     v.animate()
diff --git a/src/com/android/launcher3/folder/FolderPreviewItemAnim.java b/src/com/android/launcher3/folder/FolderPreviewItemAnim.java
index be075bc..1e56f7d 100644
--- a/src/com/android/launcher3/folder/FolderPreviewItemAnim.java
+++ b/src/com/android/launcher3/folder/FolderPreviewItemAnim.java
@@ -17,22 +17,41 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
+import android.animation.FloatArrayEvaluator;
+import android.animation.ObjectAnimator;
+import android.util.Property;
 
-import com.android.launcher3.LauncherAnimUtils;
+import java.util.Arrays;
 
 /**
  * Animates a Folder preview item.
  */
 class FolderPreviewItemAnim {
 
+    private static final Property<FolderPreviewItemAnim, float[]> PARAMS =
+            new Property<FolderPreviewItemAnim, float[]>(float[].class, "params") {
+                @Override
+                public float[] get(FolderPreviewItemAnim anim) {
+                    sTempParamsArray[0] = anim.mParams.scale;
+                    sTempParamsArray[1] = anim.mParams.transX;
+                    sTempParamsArray[2] = anim.mParams.transY;
+                    return sTempParamsArray;
+                }
+
+                @Override
+                public void set(FolderPreviewItemAnim anim, float[] value) {
+                    anim.setParams(value);
+                }
+            };
+
     private static PreviewItemDrawingParams sTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0);
+    private static final float[] sTempParamsArray = new float[3];
 
-    private ValueAnimator mValueAnimator;
+    private final ObjectAnimator mAnimator;
+    private final PreviewItemManager mItemManager;
+    private final PreviewItemDrawingParams mParams;
 
-    float finalScale;
-    float finalTransX;
-    float finalTransY;
+    public final float[] finalState;
 
     /**
      * @param params layout params to animate
@@ -43,33 +62,21 @@
      * @param duration duration in ms of the animation
      * @param onCompleteRunnable runnable to execute upon animation completion
      */
-    FolderPreviewItemAnim(final PreviewItemManager previewItemManager,
-            final PreviewItemDrawingParams params, int index0, int items0, int index1, int items1,
+    FolderPreviewItemAnim(PreviewItemManager itemManager,
+            PreviewItemDrawingParams params, int index0, int items0, int index1, int items1,
             int duration, final Runnable onCompleteRunnable) {
-        previewItemManager.computePreviewItemDrawingParams(index1, items1, sTmpParams);
+        mItemManager = itemManager;
+        mParams = params;
 
-        finalScale = sTmpParams.scale;
-        finalTransX = sTmpParams.transX;
-        finalTransY = sTmpParams.transY;
+        mItemManager.computePreviewItemDrawingParams(index1, items1, sTmpParams);
+        finalState = new float[] {sTmpParams.scale, sTmpParams.transX, sTmpParams.transY};
 
-        previewItemManager.computePreviewItemDrawingParams(index0, items0, sTmpParams);
+        mItemManager.computePreviewItemDrawingParams(index0, items0, sTmpParams);
+        float[] startState = new float[] {sTmpParams.scale, sTmpParams.transX, sTmpParams.transY};
 
-        final float scale0 = sTmpParams.scale;
-        final float transX0 = sTmpParams.transX;
-        final float transY0 = sTmpParams.transY;
-
-        mValueAnimator = LauncherAnimUtils.ofFloat(0f, 1.0f);
-        mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){
-            public void onAnimationUpdate(ValueAnimator animation) {
-                float progress = animation.getAnimatedFraction();
-
-                params.transX = transX0 + progress * (finalTransX - transX0);
-                params.transY = transY0 + progress * (finalTransY - transY0);
-                params.scale = scale0 + progress * (finalScale - scale0);
-                previewItemManager.onParamsChanged();
-            }
-        });
-        mValueAnimator.addListener(new AnimatorListenerAdapter() {
+        mAnimator = ObjectAnimator.ofObject(this, PARAMS, new FloatArrayEvaluator(),
+                startState, finalState);
+        mAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
                 if (onCompleteRunnable != null) {
@@ -78,20 +85,26 @@
                 params.anim = null;
             }
         });
-        mValueAnimator.setDuration(duration);
+        mAnimator.setDuration(duration);
+    }
+
+    private void setParams(float[] values) {
+        mParams.scale = values[0];
+        mParams.transX = values[1];
+        mParams.transY = values[2];
+        mItemManager.onParamsChanged();
     }
 
     public void start() {
-        mValueAnimator.start();
+        mAnimator.start();
     }
 
     public void cancel() {
-        mValueAnimator.cancel();
+        mAnimator.cancel();
     }
 
     public boolean hasEqualFinalState(FolderPreviewItemAnim anim) {
-        return finalTransY == anim.finalTransY && finalTransX == anim.finalTransX &&
-                finalScale == anim.finalScale;
+        return Arrays.equals(finalState, anim.finalState);
 
     }
 }
diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java
index 069ec4b..b2c0ca7 100644
--- a/src/com/android/launcher3/folder/PreviewBackground.java
+++ b/src/com/android/launcher3/folder/PreviewBackground.java
@@ -16,10 +16,15 @@
 
 package com.android.launcher3.folder;
 
+import static com.android.launcher3.graphics.IconShape.getShape;
+import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Matrix;
@@ -28,17 +33,16 @@
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
 import android.graphics.RadialGradient;
+import android.graphics.Rect;
 import android.graphics.Region;
 import android.graphics.Shader;
-import android.support.v4.graphics.ColorUtils;
 import android.util.Property;
 import android.view.View;
 
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAnimUtils;
-import com.android.launcher3.util.Themes;
+import com.android.launcher3.R;
+import com.android.launcher3.views.ActivityContext;
 
 /**
  * This object represents a FolderIcon preview background. It stores drawing / measurement
@@ -48,16 +52,6 @@
 
     private static final int CONSUMPTION_ANIMATION_DURATION = 100;
 
-    private final PorterDuffXfermode mClipPorterDuffXfermode
-            = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
-    // Create a RadialGradient such that it draws a black circle and then extends with
-    // transparent. To achieve this, we keep the gradient to black for the range [0, 1) and
-    // just at the edge quickly change it to transparent.
-    private final RadialGradient mClipShader = new RadialGradient(0, 0, 1,
-            new int[] {Color.BLACK, Color.BLACK, Color.TRANSPARENT },
-            new float[] {0, 0.999f, 1},
-            Shader.TileMode.CLAMP);
-
     private final PorterDuffXfermode mShadowPorterDuffXfermode
             = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
     private RadialGradient mShadowShader = null;
@@ -70,6 +64,8 @@
     float mScale = 1f;
     private float mColorMultiplier = 1f;
     private int mBgColor;
+    private int mStrokeColor;
+    private int mDotColor;
     private float mStrokeWidth;
     private int mStrokeAlpha = MAX_BG_OPACITY;
     private int mShadowAlpha = 255;
@@ -128,19 +124,24 @@
                 }
             };
 
-    public void setup(Launcher launcher, View invalidateDelegate,
+    public void setup(Context context, ActivityContext activity, View invalidateDelegate,
                       int availableSpaceX, int topPadding) {
         mInvalidateDelegate = invalidateDelegate;
-        mBgColor = Themes.getAttrColor(launcher, android.R.attr.colorPrimary);
 
-        DeviceProfile grid = launcher.getDeviceProfile();
+        TypedArray ta = context.getTheme().obtainStyledAttributes(R.styleable.FolderIconPreview);
+        mDotColor = ta.getColor(R.styleable.FolderIconPreview_folderDotColor, 0);
+        mStrokeColor = ta.getColor(R.styleable.FolderIconPreview_folderIconBorderColor, 0);
+        mBgColor = ta.getColor(R.styleable.FolderIconPreview_folderFillColor, 0);
+        ta.recycle();
+
+        DeviceProfile grid = activity.getWallpaperDeviceProfile();
         previewSize = grid.folderIconSizePx;
 
         basePreviewOffsetX = (availableSpaceX - previewSize) / 2;
         basePreviewOffsetY = topPadding + grid.folderIconOffsetYPx;
 
         // Stroke width is 1dp
-        mStrokeWidth = launcher.getResources().getDisplayMetrics().density;
+        mStrokeWidth = context.getResources().getDisplayMetrics().density;
 
         float radius = getScaledRadius();
         float shadowRadius = radius + mStrokeWidth;
@@ -153,6 +154,14 @@
         invalidate();
     }
 
+    void getBounds(Rect outBounds) {
+        int top = basePreviewOffsetY;
+        int left = basePreviewOffsetX;
+        int right = left + previewSize;
+        int bottom = top + previewSize;
+        outBounds.set(left, top, right, bottom);
+    }
+
     int getRadius() {
         return previewSize / 2;
     }
@@ -194,19 +203,18 @@
 
     public int getBgColor() {
         int alpha = (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier);
-        return ColorUtils.setAlphaComponent(mBgColor, alpha);
+        return setColorAlphaBound(mBgColor, alpha);
     }
 
-    public int getBadgeColor() {
-        return mBgColor;
+    public int getDotColor() {
+        return mDotColor;
     }
 
     public void drawBackground(Canvas canvas) {
         mPaint.setStyle(Paint.Style.FILL);
         mPaint.setColor(getBgColor());
 
-        drawCircle(canvas, 0 /* deltaRadius */);
-
+        getShape().drawShape(canvas, getOffsetX(), getOffsetY(), getScaledRadius(), mPaint);
         drawShadow(canvas);
     }
 
@@ -241,7 +249,7 @@
         mPaint.setShader(null);
         if (canvas.isHardwareAccelerated()) {
             mPaint.setXfermode(mShadowPorterDuffXfermode);
-            canvas.drawCircle(radius + offsetX, radius + offsetY, radius, mPaint);
+            getShape().drawShape(canvas, offsetX, offsetY, radius, mPaint);
             mPaint.setXfermode(null);
         }
 
@@ -281,10 +289,13 @@
     }
 
     public void drawBackgroundStroke(Canvas canvas) {
-        mPaint.setColor(ColorUtils.setAlphaComponent(mBgColor, mStrokeAlpha));
+        mPaint.setColor(setColorAlphaBound(mStrokeColor, mStrokeAlpha));
         mPaint.setStyle(Paint.Style.STROKE);
         mPaint.setStrokeWidth(mStrokeWidth);
-        drawCircle(canvas, 1 /* deltaRadius */);
+
+        float inset = 1f;
+        getShape().drawShape(canvas,
+                getOffsetX() + inset, getOffsetY() + inset, getScaledRadius() - inset, mPaint);
     }
 
     public void drawLeaveBehind(Canvas canvas) {
@@ -293,40 +304,17 @@
 
         mPaint.setStyle(Paint.Style.FILL);
         mPaint.setColor(Color.argb(160, 245, 245, 245));
-        drawCircle(canvas, 0 /* deltaRadius */);
+        getShape().drawShape(canvas, getOffsetX(), getOffsetY(), getScaledRadius(), mPaint);
 
         mScale = originalScale;
     }
 
-    private void drawCircle(Canvas canvas,float deltaRadius) {
-        float radius = getScaledRadius();
-        canvas.drawCircle(radius + getOffsetX(), radius + getOffsetY(),
-                radius - deltaRadius, mPaint);
-    }
-
     public Path getClipPath() {
         mPath.reset();
-        float r = getScaledRadius();
-        mPath.addCircle(r + getOffsetX(), r + getOffsetY(), r, Path.Direction.CW);
+        getShape().addToPath(mPath, getOffsetX(), getOffsetY(), getScaledRadius());
         return mPath;
     }
 
-    // It is the callers responsibility to save and restore the canvas layers.
-    void clipCanvasHardware(Canvas canvas) {
-        mPaint.setColor(Color.BLACK);
-        mPaint.setStyle(Paint.Style.FILL);
-        mPaint.setXfermode(mClipPorterDuffXfermode);
-
-        float radius = getScaledRadius();
-        mShaderMatrix.setScale(radius, radius);
-        mShaderMatrix.postTranslate(radius + getOffsetX(), radius + getOffsetY());
-        mClipShader.setLocalMatrix(mShaderMatrix);
-        mPaint.setShader(mClipShader);
-        canvas.drawPaint(mPaint);
-        mPaint.setXfermode(null);
-        mPaint.setShader(null);
-    }
-
     private void delegateDrawing(CellLayout delegate, int cellX, int cellY) {
         if (mDrawingDelegate != delegate) {
             delegate.addFolderBackground(this);
@@ -365,7 +353,7 @@
             mScaleAnimator.cancel();
         }
 
-        mScaleAnimator = LauncherAnimUtils.ofFloat(0f, 1.0f);
+        mScaleAnimator = ValueAnimator.ofFloat(0f, 1.0f);
 
         mScaleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
             @Override
@@ -397,37 +385,19 @@
         mScaleAnimator.start();
     }
 
-    public void animateToAccept(final CellLayout cl, final int cellX, final int cellY) {
-        Runnable onStart = new Runnable() {
-            @Override
-            public void run() {
-                delegateDrawing(cl, cellX, cellY);
-            }
-        };
-        animateScale(ACCEPT_SCALE_FACTOR, ACCEPT_COLOR_MULTIPLIER, onStart, null);
+    public void animateToAccept(CellLayout cl, int cellX, int cellY) {
+        animateScale(ACCEPT_SCALE_FACTOR, ACCEPT_COLOR_MULTIPLIER,
+                () -> delegateDrawing(cl, cellX, cellY), null);
     }
 
     public void animateToRest() {
         // This can be called multiple times -- we need to make sure the drawing delegate
         // is saved and restored at the beginning of the animation, since cancelling the
         // existing animation can clear the delgate.
-        final CellLayout cl = mDrawingDelegate;
-        final int cellX = delegateCellX;
-        final int cellY = delegateCellY;
-
-        Runnable onStart = new Runnable() {
-            @Override
-            public void run() {
-                delegateDrawing(cl, cellX, cellY);
-            }
-        };
-        Runnable onEnd = new Runnable() {
-            @Override
-            public void run() {
-                clearDrawingDelegate();
-            }
-        };
-        animateScale(1f, 1f, onStart, onEnd);
+        CellLayout cl = mDrawingDelegate;
+        int cellX = delegateCellX;
+        int cellY = delegateCellY;
+        animateScale(1f, 1f, () -> delegateDrawing(cl, cellX, cellY), this::clearDrawingDelegate);
     }
 
     public int getBackgroundAlpha() {
diff --git a/src/com/android/launcher3/folder/PreviewItemDrawingParams.java b/src/com/android/launcher3/folder/PreviewItemDrawingParams.java
index 607b7ca..c818462 100644
--- a/src/com/android/launcher3/folder/PreviewItemDrawingParams.java
+++ b/src/com/android/launcher3/folder/PreviewItemDrawingParams.java
@@ -40,8 +40,8 @@
         // We ensure the update will not interfere with an animation on the layout params
         // If the final values differ, we cancel the animation.
         if (anim != null) {
-            if (anim.finalTransX == transX || anim.finalTransY == transY
-                    || anim.finalScale == scale) {
+            if (anim.finalState[1] == transX || anim.finalState[2] == transY
+                    || anim.finalState[0] == scale) {
                 return;
             }
             anim.cancel();
diff --git a/src/com/android/launcher3/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java
index 1f69f6e..49763ba 100644
--- a/src/com/android/launcher3/folder/PreviewItemManager.java
+++ b/src/com/android/launcher3/folder/PreviewItemManager.java
@@ -16,27 +16,28 @@
 
 package com.android.launcher3.folder;
 
+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;
+import static com.android.launcher3.folder.FolderIcon.DROP_IN_ANIMATION_DURATION;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.support.annotation.NonNull;
 import android.view.View;
 import android.widget.TextView;
 
 import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.Utilities;
 
 import java.util.ArrayList;
 import java.util.List;
 
-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;
-import static com.android.launcher3.folder.FolderIcon.DROP_IN_ANIMATION_DURATION;
+import androidx.annotation.NonNull;
 
 /**
  * Manages the drawing and animations of {@link PreviewItemDrawingParams} for a {@link FolderIcon}.
@@ -106,7 +107,8 @@
             mTotalWidth = totalSize;
             mPrevTopPadding = mIcon.getPaddingTop();
 
-            mIcon.mBackground.setup(mIcon.mLauncher, mIcon, mTotalWidth, mIcon.getPaddingTop());
+            mIcon.mBackground.setup(mIcon.mLauncher, mIcon.mLauncher, mIcon, mTotalWidth,
+                    mIcon.getPaddingTop());
             mIcon.mPreviewLayoutRule.init(mIcon.mBackground.previewSize, mIntrinsicIconSize,
                     Utilities.isRtl(mIcon.getResources()));
 
@@ -299,7 +301,7 @@
      * @param dropped The item that was dropped onto the FolderIcon.
      */
     public void onDrop(List<BubbleTextView> oldParams, List<BubbleTextView> newParams,
-            ShortcutInfo dropped) {
+            WorkspaceItemInfo dropped) {
         int numItems = newParams.size();
         final ArrayList<PreviewItemDrawingParams> params = mFirstPageParams;
         buildParamsForPage(0, params, false);
diff --git a/src/com/android/launcher3/graphics/BitmapInfo.java b/src/com/android/launcher3/graphics/BitmapInfo.java
deleted file mode 100644
index ab906e2..0000000
--- a/src/com/android/launcher3/graphics/BitmapInfo.java
+++ /dev/null
@@ -1,43 +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 com.android.launcher3.ItemInfoWithIcon;
-
-public class BitmapInfo {
-
-    public Bitmap icon;
-    public int color;
-
-    public void applyTo(ItemInfoWithIcon info) {
-        info.iconBitmap = icon;
-        info.iconColor = color;
-    }
-
-    public void applyTo(BitmapInfo info) {
-        info.icon = icon;
-        info.color = color;
-    }
-
-    public static BitmapInfo fromBitmap(Bitmap bitmap) {
-        BitmapInfo info = new BitmapInfo();
-        info.icon = bitmap;
-        info.color = ColorExtractor.findDominantColorByHue(bitmap);
-        return info;
-    }
-}
diff --git a/src/com/android/launcher3/graphics/BitmapRenderer.java b/src/com/android/launcher3/graphics/BitmapRenderer.java
deleted file mode 100644
index 3d11c44..0000000
--- a/src/com/android/launcher3/graphics/BitmapRenderer.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.graphics;
-
-import android.annotation.TargetApi;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Picture;
-import android.os.Build;
-
-import com.android.launcher3.Utilities;
-
-public class BitmapRenderer {
-
-     public static final boolean USE_HARDWARE_BITMAP = Utilities.ATLEAST_P;
-
-     public static Bitmap createSoftwareBitmap(int width, int height, Renderer renderer) {
-          Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-          renderer.draw(new Canvas(result));
-          return result;
-     }
-
-     @TargetApi(Build.VERSION_CODES.P)
-     public static Bitmap createHardwareBitmap(int width, int height, Renderer renderer) {
-          if (!USE_HARDWARE_BITMAP) {
-               return createSoftwareBitmap(width, height, renderer);
-          }
-
-          Picture picture = new Picture();
-          renderer.draw(picture.beginRecording(width, height));
-          picture.endRecording();
-          return Bitmap.createBitmap(picture);
-     }
-
-     /**
-      * Interface representing a bitmap draw operation.
-      */
-     public interface Renderer {
-          void draw(Canvas out);
-     }
-}
diff --git a/src/com/android/launcher3/graphics/ColorExtractor.java b/src/com/android/launcher3/graphics/ColorExtractor.java
deleted file mode 100644
index e9d72b7..0000000
--- a/src/com/android/launcher3/graphics/ColorExtractor.java
+++ /dev/null
@@ -1,113 +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.Color;
-import android.util.SparseArray;
-
-/**
- * Utility class for extracting colors from a bitmap.
- */
-public class ColorExtractor {
-
-    public static int findDominantColorByHue(Bitmap bitmap) {
-        return findDominantColorByHue(bitmap, 20);
-    }
-
-    /**
-     * This picks a dominant color, looking for high-saturation, high-value, repeated hues.
-     * @param bitmap The bitmap to scan
-     * @param samples The approximate max number of samples to use.
-     */
-    public static int findDominantColorByHue(Bitmap bitmap, int samples) {
-        final int height = bitmap.getHeight();
-        final int width = bitmap.getWidth();
-        int sampleStride = (int) Math.sqrt((height * width) / samples);
-        if (sampleStride < 1) {
-            sampleStride = 1;
-        }
-
-        // This is an out-param, for getting the hsv values for an rgb
-        float[] hsv = new float[3];
-
-        // First get the best hue, by creating a histogram over 360 hue buckets,
-        // where each pixel contributes a score weighted by saturation, value, and alpha.
-        float[] hueScoreHistogram = new float[360];
-        float highScore = -1;
-        int bestHue = -1;
-
-        int[] pixels = new int[samples];
-        int pixelCount = 0;
-
-        for (int y = 0; y < height; y += sampleStride) {
-            for (int x = 0; x < width; x += sampleStride) {
-                int argb = bitmap.getPixel(x, y);
-                int alpha = 0xFF & (argb >> 24);
-                if (alpha < 0x80) {
-                    // Drop mostly-transparent pixels.
-                    continue;
-                }
-                // Remove the alpha channel.
-                int rgb = argb | 0xFF000000;
-                Color.colorToHSV(rgb, hsv);
-                // Bucket colors by the 360 integer hues.
-                int hue = (int) hsv[0];
-                if (hue < 0 || hue >= hueScoreHistogram.length) {
-                    // Defensively avoid array bounds violations.
-                    continue;
-                }
-                if (pixelCount < samples) {
-                    pixels[pixelCount++] = rgb;
-                }
-                float score = hsv[1] * hsv[2];
-                hueScoreHistogram[hue] += score;
-                if (hueScoreHistogram[hue] > highScore) {
-                    highScore = hueScoreHistogram[hue];
-                    bestHue = hue;
-                }
-            }
-        }
-
-        SparseArray<Float> rgbScores = new SparseArray<>();
-        int bestColor = 0xff000000;
-        highScore = -1;
-        // Go back over the RGB colors that match the winning hue,
-        // creating a histogram of weighted s*v scores, for up to 100*100 [s,v] buckets.
-        // The highest-scoring RGB color wins.
-        for (int i = 0; i < pixelCount; i++) {
-            int rgb = pixels[i];
-            Color.colorToHSV(rgb, hsv);
-            int hue = (int) hsv[0];
-            if (hue == bestHue) {
-                float s = hsv[1];
-                float v = hsv[2];
-                int bucket = (int) (s * 100) + (int) (v * 10000);
-                // Score by cumulative saturation * value.
-                float score = s * v;
-                Float oldTotal = rgbScores.get(bucket);
-                float newTotal = oldTotal == null ? score : oldTotal + score;
-                rgbScores.put(bucket, newTotal);
-                if (newTotal > highScore) {
-                    highScore = newTotal;
-                    // All the colors in the winning bucket are very similar. Last in wins.
-                    bestColor = rgb;
-                }
-            }
-        }
-        return bestColor;
-    }
-}
diff --git a/src/com/android/launcher3/graphics/ColorScrim.java b/src/com/android/launcher3/graphics/ColorScrim.java
deleted file mode 100644
index 96d93d8..0000000
--- a/src/com/android/launcher3/graphics/ColorScrim.java
+++ /dev/null
@@ -1,64 +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 android.graphics.Canvas;
-import android.graphics.Color;
-import android.support.v4.graphics.ColorUtils;
-import android.view.View;
-import android.view.animation.Interpolator;
-
-import com.android.launcher3.R;
-import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.uioverrides.WallpaperColorInfo;
-
-/**
- * Simple scrim which draws a color
- */
-public class ColorScrim extends ViewScrim {
-
-    private final int mColor;
-    private final Interpolator mInterpolator;
-    private int mCurrentColor;
-
-    public ColorScrim(View view, int color, Interpolator interpolator) {
-        super(view);
-        mColor = color;
-        mInterpolator = interpolator;
-    }
-
-    @Override
-    protected void onProgressChanged() {
-        mCurrentColor = ColorUtils.setAlphaComponent(mColor,
-                Math.round(mInterpolator.getInterpolation(mProgress) * Color.alpha(mColor)));
-    }
-
-    @Override
-    public void draw(Canvas canvas, int width, int height) {
-        if (mProgress > 0) {
-            canvas.drawColor(mCurrentColor);
-        }
-    }
-
-    public static ColorScrim createExtractedColorScrim(View view) {
-        WallpaperColorInfo colors = WallpaperColorInfo.getInstance(view.getContext());
-        int alpha = view.getResources().getInteger(R.integer.extracted_color_gradient_alpha);
-        ColorScrim scrim = new ColorScrim(view, ColorUtils.setAlphaComponent(
-                colors.getSecondaryColor(), alpha), Interpolators.LINEAR);
-        scrim.attach();
-        return scrim;
-    }
-}
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index 5094280..7eb4015 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -33,8 +33,10 @@
 import com.android.launcher3.R;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.icons.BitmapRenderer;
 import com.android.launcher3.util.UiThreadHelper;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
+import com.android.launcher3.widget.PendingAppWidgetHostView;
 
 import java.nio.ByteBuffer;
 
@@ -130,9 +132,15 @@
             width = (int) (mView.getWidth() * scale);
             height = (int) (mView.getHeight() * scale);
 
-            // Use software renderer for widgets as we know that they already work
-            return BitmapRenderer.createSoftwareBitmap(width + blurSizeOutline,
-                    height + blurSizeOutline, (c) -> drawDragView(c, scale));
+            if (mView instanceof PendingAppWidgetHostView) {
+                // Use hardware renderer as the icon for the pending app widget may be a hw bitmap
+                return BitmapRenderer.createHardwareBitmap(width + blurSizeOutline,
+                        height + blurSizeOutline, (c) -> drawDragView(c, scale));
+            } else {
+                // Use software renderer for widgets as we know that they already work
+                return BitmapRenderer.createSoftwareBitmap(width + blurSizeOutline,
+                        height + blurSizeOutline, (c) -> drawDragView(c, scale));
+            }
         }
 
         return BitmapRenderer.createHardwareBitmap(width + blurSizeOutline,
diff --git a/src/com/android/launcher3/graphics/DrawableFactory.java b/src/com/android/launcher3/graphics/DrawableFactory.java
index 34a4e2d..c9566cb 100644
--- a/src/com/android/launcher3/graphics/DrawableFactory.java
+++ b/src/com/android/launcher3/graphics/DrawableFactory.java
@@ -16,48 +16,38 @@
 
 package com.android.launcher3.graphics;
 
+import static com.android.launcher3.graphics.IconShape.getShapePath;
+
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
-import android.graphics.Path;
 import android.graphics.Rect;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Process;
 import android.os.UserHandle;
-import android.support.annotation.UiThread;
 import android.util.ArrayMap;
-import android.util.Log;
+
 import com.android.launcher3.FastBitmapDrawable;
 import com.android.launcher3.ItemInfoWithIcon;
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.AllAppsBackgroundDrawable;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.ResourceBasedOverride;
+
+import androidx.annotation.UiThread;
 
 /**
  * Factory for creating new drawables.
  */
-public class DrawableFactory {
+public class DrawableFactory implements ResourceBasedOverride {
 
-    private static final String TAG = "DrawableFactory";
-
-    private static DrawableFactory sInstance;
-    private static final Object LOCK = new Object();
-
-    private Path mPreloadProgressPath;
-
-    public static DrawableFactory get(Context context) {
-        synchronized (LOCK) {
-            if (sInstance == null) {
-                sInstance = Utilities.getOverrideObject(DrawableFactory.class,
-                        context.getApplicationContext(), R.string.drawable_factory_class);
-            }
-            return sInstance;
-        }
-    }
+    public static final MainThreadInitializedObject<DrawableFactory> INSTANCE =
+            new MainThreadInitializedObject<>(c -> Overrides.getObject(DrawableFactory.class,
+                        c.getApplicationContext(), R.string.drawable_factory_class));
 
     protected final UserHandle mMyUser = Process.myUserHandle();
     protected final ArrayMap<UserHandle, Bitmap> mUserBadges = new ArrayMap<>();
@@ -65,48 +55,25 @@
     /**
      * Returns a FastBitmapDrawable with the icon.
      */
-    public FastBitmapDrawable newIcon(ItemInfoWithIcon info) {
-        FastBitmapDrawable drawable = new FastBitmapDrawable(info);
+    public FastBitmapDrawable newIcon(Context context, ItemInfoWithIcon info) {
+        FastBitmapDrawable drawable = info.usingLowResIcon()
+                ? new PlaceHolderIconDrawable(info, getShapePath(), context)
+                : new FastBitmapDrawable(info);
         drawable.setIsDisabled(info.isDisabled());
         return drawable;
     }
 
-    public FastBitmapDrawable newIcon(BitmapInfo info, ActivityInfo target) {
-        return new FastBitmapDrawable(info);
+    public FastBitmapDrawable newIcon(Context context, BitmapInfo info, ActivityInfo target) {
+        return info.isLowRes()
+                ? new PlaceHolderIconDrawable(info, getShapePath(), context)
+                : new FastBitmapDrawable(info);
     }
 
     /**
      * Returns a FastBitmapDrawable with the icon.
      */
-    public PreloadIconDrawable newPendingIcon(ItemInfoWithIcon info, Context context) {
-        if (mPreloadProgressPath == null) {
-            mPreloadProgressPath = getPreloadProgressPath(context);
-        }
-        return new PreloadIconDrawable(info, mPreloadProgressPath, context);
-    }
-
-    protected Path getPreloadProgressPath(Context context) {
-        if (Utilities.ATLEAST_OREO) {
-            try {
-                // Try to load the path from Mask Icon
-                Drawable icon = context.getDrawable(R.drawable.adaptive_icon_drawable_wrapper);
-                icon.setBounds(0, 0,
-                        PreloadIconDrawable.PATH_SIZE, PreloadIconDrawable.PATH_SIZE);
-                return (Path) icon.getClass().getMethod("getIconMask").invoke(icon);
-            } catch (Exception e) {
-                Log.e(TAG, "Error loading mask icon", e);
-            }
-        }
-
-        // Create a circle static from top center and going clockwise.
-        Path p = new Path();
-        p.moveTo(PreloadIconDrawable.PATH_SIZE / 2, 0);
-        p.addArc(0, 0, PreloadIconDrawable.PATH_SIZE, PreloadIconDrawable.PATH_SIZE, -90, 360);
-        return p;
-    }
-
-    public AllAppsBackgroundDrawable getAllAppsBackground(Context context) {
-        return new AllAppsBackgroundDrawable(context);
+    public PreloadIconDrawable newPendingIcon(Context context, ItemInfoWithIcon info) {
+        return new PreloadIconDrawable(info, getShapePath(), context);
     }
 
     /**
diff --git a/src/com/android/launcher3/graphics/FixedScaleDrawable.java b/src/com/android/launcher3/graphics/FixedScaleDrawable.java
deleted file mode 100644
index 0f0e424..0000000
--- a/src/com/android/launcher3/graphics/FixedScaleDrawable.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package com.android.launcher3.graphics;
-
-import android.annotation.TargetApi;
-import android.content.res.Resources;
-import android.content.res.Resources.Theme;
-import android.graphics.Canvas;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.DrawableWrapper;
-import android.os.Build;
-import android.util.AttributeSet;
-
-import org.xmlpull.v1.XmlPullParser;
-
-/**
- * Extension of {@link DrawableWrapper} which scales the child drawables by a fixed amount.
- */
-@TargetApi(Build.VERSION_CODES.N)
-public class FixedScaleDrawable extends DrawableWrapper {
-
-    // TODO b/33553066 use the constant defined in MaskableIconDrawable
-    private static final float LEGACY_ICON_SCALE = .7f * .6667f;
-    private float mScaleX, mScaleY;
-
-    public FixedScaleDrawable() {
-        super(new ColorDrawable());
-        mScaleX = LEGACY_ICON_SCALE;
-        mScaleY = LEGACY_ICON_SCALE;
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        int saveCount = canvas.save();
-        canvas.scale(mScaleX, mScaleY,
-                getBounds().exactCenterX(), getBounds().exactCenterY());
-        super.draw(canvas);
-        canvas.restoreToCount(saveCount);
-    }
-
-    @Override
-    public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs) { }
-
-    @Override
-    public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) { }
-
-    public void setScale(float scale) {
-        float h = getIntrinsicHeight();
-        float w = getIntrinsicWidth();
-        mScaleX = scale * LEGACY_ICON_SCALE;
-        mScaleY = scale * LEGACY_ICON_SCALE;
-        if (h > w && w > 0) {
-            mScaleX *= w / h;
-        } else if (w > h && h > 0) {
-            mScaleY *= h / w;
-        }
-    }
-}
diff --git a/src/com/android/launcher3/graphics/FragmentWithPreview.java b/src/com/android/launcher3/graphics/FragmentWithPreview.java
new file mode 100644
index 0000000..250eca2
--- /dev/null
+++ b/src/com/android/launcher3/graphics/FragmentWithPreview.java
@@ -0,0 +1,48 @@
+/*
+ * 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 android.app.Fragment;
+import android.content.Context;
+import android.os.Bundle;
+
+/**
+ * Extension of fragment, with support for preview mode.
+ */
+public class FragmentWithPreview extends Fragment {
+
+    private Context mPreviewContext;
+
+    public final void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        onInit(savedInstanceState);
+    }
+
+    public void onInit(Bundle savedInstanceState) { }
+
+
+    public Context getContext() {
+        return mPreviewContext != null ? mPreviewContext : getActivity();
+    }
+
+    void enterPreviewMode(Context context) {
+        mPreviewContext = context;
+    }
+
+    public boolean isInPreviewMode() {
+        return mPreviewContext != null;
+    }
+}
diff --git a/src/com/android/launcher3/graphics/GridOptionsProvider.java b/src/com/android/launcher3/graphics/GridOptionsProvider.java
new file mode 100644
index 0000000..efd39ee
--- /dev/null
+++ b/src/com/android/launcher3/graphics/GridOptionsProvider.java
@@ -0,0 +1,191 @@
+package com.android.launcher3.graphics;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.res.XmlResourceParser;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
+import android.text.TextUtils;
+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.util.LooperExecutor;
+import com.android.launcher3.util.UiThreadHelper;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Future;
+
+/**
+ * Exposes various launcher grid options and allows the caller to change them.
+ * APIs:
+ *      /list_options: List the various available grip options, has following columns
+ *          name: name of the grid
+ *          rows: number of rows in the grid
+ *          cols: number of columns in the grid
+ *          preview_count: number of previews available for this grid option. The preview uri
+ *                         looks like /preview/<grid-name>/<preview index starting with 0>
+ *          is_default: true if this grid is currently active
+ *
+ *     /preview: Opens a file stream for the grid preview
+ *
+ *     /default_grid: Call update to set the current grid, with values
+ *          name: name of the grid to apply
+ */
+public class GridOptionsProvider extends ContentProvider {
+
+    private static final String TAG = "GridOptionsProvider";
+
+    private static final String KEY_NAME = "name";
+    private static final String KEY_ROWS = "rows";
+    private static final String KEY_COLS = "cols";
+    private static final String KEY_PREVIEW_COUNT = "preview_count";
+    private static final String KEY_IS_DEFAULT = "is_default";
+
+    private static final String KEY_LIST_OPTIONS = "/list_options";
+    private static final String KEY_DEFAULT_GRID = "/default_grid";
+
+    private static final String KEY_PREVIEW = "preview";
+    private static final String MIME_TYPE_PNG = "image/png";
+
+    public static final PipeDataWriter<Future<Bitmap>> BITMAP_WRITER =
+            new PipeDataWriter<Future<Bitmap>>() {
+                @Override
+                public void writeDataToPipe(ParcelFileDescriptor output, Uri uri, String s,
+                        Bundle bundle, Future<Bitmap> bitmap) {
+                    try (AutoCloseOutputStream os = new AutoCloseOutputStream(output)) {
+                        bitmap.get().compress(Bitmap.CompressFormat.PNG, 100, os);
+                    } catch (Exception e) {
+                        Log.w(TAG, "fail to write to pipe", e);
+                    }
+                }
+            };
+
+    @Override
+    public boolean onCreate() {
+        return true;
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection,
+            String[] selectionArgs, String sortOrder) {
+        if (!KEY_LIST_OPTIONS.equals(uri.getPath())) {
+            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() {
+        List<GridOption> result = new ArrayList<>();
+        try (XmlResourceParser parser = getContext().getResources().getXml(R.xml.device_profiles)) {
+            final int depth = parser.getDepth();
+            int type;
+            while (((type = parser.next()) != XmlPullParser.END_TAG ||
+                    parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
+                if ((type == XmlPullParser.START_TAG)
+                        && GridOption.TAG_NAME.equals(parser.getName())) {
+                    result.add(new GridOption(getContext(), Xml.asAttributeSet(parser)));
+                }
+            }
+        } catch (IOException | XmlPullParserException e) {
+            Log.e(TAG, "Error parsing device profile", e);
+            return Collections.emptyList();
+        }
+        return result;
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        List<String> segments = uri.getPathSegments();
+        if (segments.size() > 0 && KEY_PREVIEW.equals(segments.get(0))) {
+            return MIME_TYPE_PNG;
+        }
+        return "vnd.android.cursor.dir/launcher_grid";
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues initialValues) {
+        return null;
+    }
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        return 0;
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+        if (!KEY_DEFAULT_GRID.equals(uri.getPath())) {
+            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;
+            }
+        }
+        if (match == null) {
+            return 0;
+        }
+
+        InvariantDeviceProfile.INSTANCE.get(getContext()).setCurrentGrid(getContext(), gridName);
+        return 1;
+    }
+
+    @Override
+    public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
+        List<String> segments = uri.getPathSegments();
+        if (segments.size() < 2 || !KEY_PREVIEW.equals(segments.get(0))) {
+            throw new FileNotFoundException("Invalid preview url");
+        }
+        String profileName = segments.get(1);
+        if (TextUtils.isEmpty(profileName)) {
+            throw new FileNotFoundException("Invalid preview url");
+        }
+
+        InvariantDeviceProfile idp;
+        try {
+            idp = new InvariantDeviceProfile(getContext(), profileName);
+        } catch (Exception e) {
+            throw new FileNotFoundException(e.getMessage());
+        }
+
+        LooperExecutor executor = new LooperExecutor(UiThreadHelper.getBackgroundLooper());
+        try {
+            return openPipeHelper(uri, MIME_TYPE_PNG, null,
+                    executor.submit(new LauncherPreviewRenderer(getContext(), idp)), BITMAP_WRITER);
+        } catch (Exception e) {
+            throw new FileNotFoundException(e.getMessage());
+        }
+    }
+}
diff --git a/src/com/android/launcher3/graphics/IconNormalizer.java b/src/com/android/launcher3/graphics/IconNormalizer.java
deleted file mode 100644
index a2a0801..0000000
--- a/src/com/android/launcher3/graphics/IconNormalizer.java
+++ /dev/null
@@ -1,385 +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.graphics;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.util.Log;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
-
-import java.nio.ByteBuffer;
-
-public class IconNormalizer {
-
-    private static final String TAG = "IconNormalizer";
-    private static final boolean DEBUG = false;
-    // Ratio of icon visible area to full icon size for a square shaped icon
-    private static final float MAX_SQUARE_AREA_FACTOR = 375.0f / 576;
-    // Ratio of icon visible area to full icon size for a circular shaped icon
-    private static final float MAX_CIRCLE_AREA_FACTOR = 380.0f / 576;
-
-    private static final float CIRCLE_AREA_BY_RECT = (float) Math.PI / 4;
-
-    // Slope used to calculate icon visible area to full icon size for any generic shaped icon.
-    private static final float LINEAR_SCALE_SLOPE =
-            (MAX_CIRCLE_AREA_FACTOR - MAX_SQUARE_AREA_FACTOR) / (1 - CIRCLE_AREA_BY_RECT);
-
-    private static final int MIN_VISIBLE_ALPHA = 40;
-
-    // Shape detection related constants
-    private static final float BOUND_RATIO_MARGIN = .05f;
-    private static final float PIXEL_DIFF_PERCENTAGE_THRESHOLD = 0.005f;
-    private static final float SCALE_NOT_INITIALIZED = 0;
-
-    // Ratio of the diameter of an normalized circular icon to the actual icon size.
-    public static final float ICON_VISIBLE_AREA_FACTOR = 0.92f;
-
-    private final int mMaxSize;
-    private final Bitmap mBitmap;
-    private final Canvas mCanvas;
-    private final Paint mPaintMaskShape;
-    private final Paint mPaintMaskShapeOutline;
-    private final byte[] mPixels;
-
-    private final Rect mAdaptiveIconBounds;
-    private float mAdaptiveIconScale;
-
-    // for each y, stores the position of the leftmost x and the rightmost x
-    private final float[] mLeftBorder;
-    private final float[] mRightBorder;
-    private final Rect mBounds;
-    private final Path mShapePath;
-    private final Matrix mMatrix;
-
-    /** package private **/
-    IconNormalizer(Context context) {
-        // Use twice the icon size as maximum size to avoid scaling down twice.
-        mMaxSize = LauncherAppState.getIDP(context).iconBitmapSize * 2;
-        mBitmap = Bitmap.createBitmap(mMaxSize, mMaxSize, Bitmap.Config.ALPHA_8);
-        mCanvas = new Canvas(mBitmap);
-        mPixels = new byte[mMaxSize * mMaxSize];
-        mLeftBorder = new float[mMaxSize];
-        mRightBorder = new float[mMaxSize];
-        mBounds = new Rect();
-        mAdaptiveIconBounds = new Rect();
-
-        mPaintMaskShape = new Paint();
-        mPaintMaskShape.setColor(Color.RED);
-        mPaintMaskShape.setStyle(Paint.Style.FILL);
-        mPaintMaskShape.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
-
-        mPaintMaskShapeOutline = new Paint();
-        mPaintMaskShapeOutline.setStrokeWidth(2 * context.getResources().getDisplayMetrics().density);
-        mPaintMaskShapeOutline.setStyle(Paint.Style.STROKE);
-        mPaintMaskShapeOutline.setColor(Color.BLACK);
-        mPaintMaskShapeOutline.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
-
-        mShapePath = new Path();
-        mMatrix = new Matrix();
-        mAdaptiveIconScale = SCALE_NOT_INITIALIZED;
-    }
-
-    /**
-     * Returns if the shape of the icon is same as the path.
-     * For this method to work, the shape path bounds should be in [0,1]x[0,1] bounds.
-     */
-    private boolean isShape(Path maskPath) {
-        // Condition1:
-        // If width and height of the path not close to a square, then the icon shape is
-        // not same as the mask shape.
-        float iconRatio = ((float) mBounds.width()) / mBounds.height();
-        if (Math.abs(iconRatio - 1) > BOUND_RATIO_MARGIN) {
-            if (DEBUG) {
-                Log.d(TAG, "Not same as mask shape because width != height. " + iconRatio);
-            }
-            return false;
-        }
-
-        // Condition 2:
-        // Actual icon (white) and the fitted shape (e.g., circle)(red) XOR operation
-        // should generate transparent image, if the actual icon is equivalent to the shape.
-
-        // Fit the shape within the icon's bounding box
-        mMatrix.reset();
-        mMatrix.setScale(mBounds.width(), mBounds.height());
-        mMatrix.postTranslate(mBounds.left, mBounds.top);
-        maskPath.transform(mMatrix, mShapePath);
-
-        // XOR operation
-        mCanvas.drawPath(mShapePath, mPaintMaskShape);
-
-        // DST_OUT operation around the mask path outline
-        mCanvas.drawPath(mShapePath, mPaintMaskShapeOutline);
-
-        // Check if the result is almost transparent
-        return isTransparentBitmap();
-    }
-
-    /**
-     * Used to determine if certain the bitmap is transparent.
-     */
-    private boolean isTransparentBitmap() {
-        ByteBuffer buffer = ByteBuffer.wrap(mPixels);
-        buffer.rewind();
-        mBitmap.copyPixelsToBuffer(buffer);
-
-        int y = mBounds.top;
-        // buffer position
-        int index = y * mMaxSize;
-        // buffer shift after every row, width of buffer = mMaxSize
-        int rowSizeDiff = mMaxSize - mBounds.right;
-
-        int sum = 0;
-        for (; y < mBounds.bottom; y++) {
-            index += mBounds.left;
-            for (int x = mBounds.left; x < mBounds.right; x++) {
-                if ((mPixels[index] & 0xFF) > MIN_VISIBLE_ALPHA) {
-                    sum++;
-                }
-                index++;
-            }
-            index += rowSizeDiff;
-        }
-
-        float percentageDiffPixels = ((float) sum) / (mBounds.width() * mBounds.height());
-        return percentageDiffPixels < PIXEL_DIFF_PERCENTAGE_THRESHOLD;
-    }
-
-    /**
-     * Returns the amount by which the {@param d} should be scaled (in both dimensions) so that it
-     * matches the design guidelines for a launcher icon.
-     *
-     * We first calculate the convex hull of the visible portion of the icon.
-     * This hull then compared with the bounding rectangle of the hull to find how closely it
-     * resembles a circle and a square, by comparing the ratio of the areas. Note that this is not an
-     * ideal solution but it gives satisfactory result without affecting the performance.
-     *
-     * This closeness is used to determine the ratio of hull area to the full icon size.
-     * Refer {@link #MAX_CIRCLE_AREA_FACTOR} and {@link #MAX_SQUARE_AREA_FACTOR}
-     *
-     * @param outBounds optional rect to receive the fraction distance from each edge.
-     */
-    public synchronized float getScale(@NonNull Drawable d, @Nullable RectF outBounds,
-            @Nullable Path path, @Nullable boolean[] outMaskShape) {
-        if (Utilities.ATLEAST_OREO && d instanceof AdaptiveIconDrawable) {
-            if (mAdaptiveIconScale != SCALE_NOT_INITIALIZED) {
-                if (outBounds != null) {
-                    outBounds.set(mAdaptiveIconBounds);
-                }
-                return mAdaptiveIconScale;
-            }
-            if (d instanceof FolderAdaptiveIcon) {
-                // Since we just want the scale, avoid heavy drawing operations
-                d = new AdaptiveIconDrawable(new ColorDrawable(Color.BLACK), null);
-            }
-        }
-        int width = d.getIntrinsicWidth();
-        int height = d.getIntrinsicHeight();
-        if (width <= 0 || height <= 0) {
-            width = width <= 0 || width > mMaxSize ? mMaxSize : width;
-            height = height <= 0 || height > mMaxSize ? mMaxSize : height;
-        } else if (width > mMaxSize || height > mMaxSize) {
-            int max = Math.max(width, height);
-            width = mMaxSize * width / max;
-            height = mMaxSize * height / max;
-        }
-
-        mBitmap.eraseColor(Color.TRANSPARENT);
-        d.setBounds(0, 0, width, height);
-        d.draw(mCanvas);
-
-        ByteBuffer buffer = ByteBuffer.wrap(mPixels);
-        buffer.rewind();
-        mBitmap.copyPixelsToBuffer(buffer);
-
-        // Overall bounds of the visible icon.
-        int topY = -1;
-        int bottomY = -1;
-        int leftX = mMaxSize + 1;
-        int rightX = -1;
-
-        // Create border by going through all pixels one row at a time and for each row find
-        // the first and the last non-transparent pixel. Set those values to mLeftBorder and
-        // mRightBorder and use -1 if there are no visible pixel in the row.
-
-        // buffer position
-        int index = 0;
-        // buffer shift after every row, width of buffer = mMaxSize
-        int rowSizeDiff = mMaxSize - width;
-        // first and last position for any row.
-        int firstX, lastX;
-
-        for (int y = 0; y < height; y++) {
-            firstX = lastX = -1;
-            for (int x = 0; x < width; x++) {
-                if ((mPixels[index] & 0xFF) > MIN_VISIBLE_ALPHA) {
-                    if (firstX == -1) {
-                        firstX = x;
-                    }
-                    lastX = x;
-                }
-                index++;
-            }
-            index += rowSizeDiff;
-
-            mLeftBorder[y] = firstX;
-            mRightBorder[y] = lastX;
-
-            // If there is at least one visible pixel, update the overall bounds.
-            if (firstX != -1) {
-                bottomY = y;
-                if (topY == -1) {
-                    topY = y;
-                }
-
-                leftX = Math.min(leftX, firstX);
-                rightX = Math.max(rightX, lastX);
-            }
-        }
-
-        if (topY == -1 || rightX == -1) {
-            // No valid pixels found. Do not scale.
-            return 1;
-        }
-
-        convertToConvexArray(mLeftBorder, 1, topY, bottomY);
-        convertToConvexArray(mRightBorder, -1, topY, bottomY);
-
-        // Area of the convex hull
-        float area = 0;
-        for (int y = 0; y < height; y++) {
-            if (mLeftBorder[y] <= -1) {
-                continue;
-            }
-            area += mRightBorder[y] - mLeftBorder[y] + 1;
-        }
-
-        // Area of the rectangle required to fit the convex hull
-        float rectArea = (bottomY + 1 - topY) * (rightX + 1 - leftX);
-        float hullByRect = area / rectArea;
-
-        float scaleRequired;
-        if (hullByRect < CIRCLE_AREA_BY_RECT) {
-            scaleRequired = MAX_CIRCLE_AREA_FACTOR;
-        } else {
-            scaleRequired = MAX_SQUARE_AREA_FACTOR + LINEAR_SCALE_SLOPE * (1 - hullByRect);
-        }
-        mBounds.left = leftX;
-        mBounds.right = rightX;
-
-        mBounds.top = topY;
-        mBounds.bottom = bottomY;
-
-        if (outBounds != null) {
-            outBounds.set(((float) mBounds.left) / width, ((float) mBounds.top) / height,
-                    1 - ((float) mBounds.right) / width,
-                    1 - ((float) mBounds.bottom) / height);
-        }
-
-        if (outMaskShape != null && outMaskShape.length > 0) {
-            outMaskShape[0] = isShape(path);
-        }
-        float areaScale = area / (width * height);
-        // Use sqrt of the final ratio as the images is scaled across both width and height.
-        float scale = areaScale > scaleRequired ? (float) Math.sqrt(scaleRequired / areaScale) : 1;
-        if (Utilities.ATLEAST_OREO && d instanceof AdaptiveIconDrawable &&
-                mAdaptiveIconScale == SCALE_NOT_INITIALIZED) {
-            mAdaptiveIconScale = scale;
-            mAdaptiveIconBounds.set(mBounds);
-        }
-        return scale;
-    }
-
-    /**
-     * Modifies {@param xCoordinates} to represent a convex border. Fills in all missing values
-     * (except on either ends) with appropriate values.
-     * @param xCoordinates map of x coordinate per y.
-     * @param direction 1 for left border and -1 for right border.
-     * @param topY the first Y position (inclusive) with a valid value.
-     * @param bottomY the last Y position (inclusive) with a valid value.
-     */
-    private static void convertToConvexArray(
-            float[] xCoordinates, int direction, int topY, int bottomY) {
-        int total = xCoordinates.length;
-        // The tangent at each pixel.
-        float[] angles = new float[total - 1];
-
-        int first = topY; // First valid y coordinate
-        int last = -1;    // Last valid y coordinate which didn't have a missing value
-
-        float lastAngle = Float.MAX_VALUE;
-
-        for (int i = topY + 1; i <= bottomY; i++) {
-            if (xCoordinates[i] <= -1) {
-                continue;
-            }
-            int start;
-
-            if (lastAngle == Float.MAX_VALUE) {
-                start = first;
-            } else {
-                float currentAngle = (xCoordinates[i] - xCoordinates[last]) / (i - last);
-                start = last;
-                // If this position creates a concave angle, keep moving up until we find a
-                // position which creates a convex angle.
-                if ((currentAngle - lastAngle) * direction < 0) {
-                    while (start > first) {
-                        start --;
-                        currentAngle = (xCoordinates[i] - xCoordinates[start]) / (i - start);
-                        if ((currentAngle - angles[start]) * direction >= 0) {
-                            break;
-                        }
-                    }
-                }
-            }
-
-            // Reset from last check
-            lastAngle = (xCoordinates[i] - xCoordinates[start]) / (i - start);
-            // Update all the points from start.
-            for (int j = start; j < i; j++) {
-                angles[j] = lastAngle;
-                xCoordinates[j] = xCoordinates[start] + lastAngle * (j - start);
-            }
-            last = i;
-        }
-    }
-
-    /**
-     * @return The diameter of the normalized circle that fits inside of the square (size x size).
-     */
-    public static int getNormalizedCircleSize(int size) {
-        float area = size * size * MAX_CIRCLE_AREA_FACTOR;
-        return (int) Math.round(Math.sqrt((4 * area) / Math.PI));
-    }
-}
diff --git a/src/com/android/launcher3/graphics/IconPalette.java b/src/com/android/launcher3/graphics/IconPalette.java
index 9c3b77e..3d4a100 100644
--- a/src/com/android/launcher3/graphics/IconPalette.java
+++ b/src/com/android/launcher3/graphics/IconPalette.java
@@ -16,15 +16,18 @@
 
 package com.android.launcher3.graphics;
 
+import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
+
 import android.app.Notification;
 import android.content.Context;
 import android.graphics.Color;
-import android.support.v4.graphics.ColorUtils;
 import android.util.Log;
 
 import com.android.launcher3.R;
 import com.android.launcher3.util.Themes;
 
+import androidx.core.graphics.ColorUtils;
+
 /**
  * Contains colors based on the dominant color of an icon.
  */
@@ -146,7 +149,7 @@
     }
 
     public static int getMutedColor(int color, float whiteScrimAlpha) {
-        int whiteScrim = ColorUtils.setAlphaComponent(Color.WHITE, (int) (255 * whiteScrimAlpha));
+        int whiteScrim = setColorAlphaBound(Color.WHITE, (int) (255 * whiteScrimAlpha));
         return ColorUtils.compositeColors(whiteScrim, color);
     }
 }
diff --git a/src/com/android/launcher3/graphics/IconShape.java b/src/com/android/launcher3/graphics/IconShape.java
new file mode 100644
index 0000000..4369385
--- /dev/null
+++ b/src/com/android/launcher3/graphics/IconShape.java
@@ -0,0 +1,478 @@
+/*
+ * 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 com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.FloatArrayEvaluator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.Region.Op;
+import android.graphics.drawable.AdaptiveIconDrawable;
+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;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+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;
+    };
+
+    public abstract void drawShape(Canvas canvas, float offsetX, float offsetY, float radius,
+            Paint paint);
+
+    public abstract void addToPath(Path path, float offsetX, float offsetY, float radius);
+
+    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
+     */
+    private static abstract class SimpleRectShape extends IconShape {
+
+        @Override
+        public final <T extends View & ClipPathView> Animator createRevealAnimator(T target,
+                Rect startRect, Rect endRect, float endRadius, boolean isReversed) {
+            return new RoundedRectRevealOutlineProvider(
+                    getStartRadius(startRect), endRadius, startRect, endRect) {
+                @Override
+                public boolean shouldRemoveElevationDuringAnimation() {
+                    return true;
+                }
+            }.createRevealAnimator(target, isReversed);
+        }
+
+        protected abstract float getStartRadius(Rect startRect);
+    }
+
+    /**
+     * Abstract shape which draws using {@link Path}
+     */
+    private static abstract class PathShape extends IconShape {
+
+        private final Path mTmpPath = new Path();
+
+        @Override
+        public final void drawShape(Canvas canvas, float offsetX, float offsetY, float radius,
+                Paint paint) {
+            mTmpPath.reset();
+            addToPath(mTmpPath, offsetX, offsetY, radius);
+            canvas.drawPath(mTmpPath, paint);
+        }
+
+        protected abstract AnimatorUpdateListener newUpdateListener(
+                Rect startRect, Rect endRect, float endRadius, Path outPath);
+
+        @Override
+        public final <T extends View & ClipPathView> Animator createRevealAnimator(T target,
+                Rect startRect, Rect endRect, float endRadius, boolean isReversed) {
+            Path path = new Path();
+            AnimatorUpdateListener listener =
+                    newUpdateListener(startRect, endRect, endRadius, path);
+
+            ValueAnimator va =
+                    isReversed ? ValueAnimator.ofFloat(1f, 0f) : ValueAnimator.ofFloat(0f, 1f);
+            va.addListener(new AnimatorListenerAdapter() {
+                private ViewOutlineProvider mOldOutlineProvider;
+
+                public void onAnimationStart(Animator animation) {
+                    mOldOutlineProvider = target.getOutlineProvider();
+                    target.setOutlineProvider(null);
+
+                    target.setTranslationZ(-target.getElevation());
+                }
+
+                public void onAnimationEnd(Animator animation) {
+                    target.setTranslationZ(0);
+                    target.setClipPath(null);
+                    target.setOutlineProvider(mOldOutlineProvider);
+                }
+            });
+
+            va.addUpdateListener((anim) -> {
+                path.reset();
+                listener.onAnimationUpdate(anim);
+                target.setClipPath(path);
+            });
+
+            return va;
+        }
+    }
+
+    public static final class Circle extends SimpleRectShape {
+
+        @Override
+        public void drawShape(Canvas canvas, float offsetX, float offsetY, float radius, Paint p) {
+            canvas.drawCircle(radius + offsetX, radius + offsetY, radius, p);
+        }
+
+        @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;
+        }
+
+        @Override
+        public boolean enableShapeDetection() {
+            return true;
+        }
+    }
+
+    public static class RoundedSquare extends SimpleRectShape {
+
+        /**
+         * Ratio of corner radius to half size.
+         */
+        private final float mRadiusRatio;
+
+        public RoundedSquare(float radiusRatio) {
+            mRadiusRatio = radiusRatio;
+        }
+
+        @Override
+        public void drawShape(Canvas canvas, float offsetX, float offsetY, float radius, Paint p) {
+            float cx = radius + offsetX;
+            float cy = radius + offsetY;
+            float cr = radius * mRadiusRatio;
+            canvas.drawRoundRect(cx - radius, cy - radius, cx + radius, cy + radius, cr, cr, p);
+        }
+
+        @Override
+        public void addToPath(Path path, float offsetX, float offsetY, float radius) {
+            float cx = radius + offsetX;
+            float cy = radius + offsetY;
+            float cr = radius * mRadiusRatio;
+            path.addRoundRect(cx - radius, cy - radius, cx + radius, cy + radius, cr, cr,
+                    Path.Direction.CW);
+        }
+
+        @Override
+        protected float getStartRadius(Rect startRect) {
+            return (startRect.width() / 2f) * mRadiusRatio;
+        }
+    }
+
+    public static class TearDrop extends PathShape {
+
+        /**
+         * Radio of short radius to large radius, based on the shape options defined in the config.
+         */
+        private final float mRadiusRatio;
+        private final float[] mTempRadii = new float[8];
+
+        public TearDrop(float radiusRatio) {
+            mRadiusRatio = radiusRatio;
+        }
+
+        @Override
+        public void addToPath(Path p, float offsetX, float offsetY, float r1) {
+            float r2 = r1 * mRadiusRatio;
+            float cx = r1 + offsetX;
+            float cy = r1 + offsetY;
+
+            p.addRoundRect(cx - r1, cy - r1, cx + r1, cy + r1, getRadiiArray(r1, r2),
+                    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
+        protected AnimatorUpdateListener newUpdateListener(Rect startRect, Rect endRect,
+                float endRadius, Path outPath) {
+            float r1 = startRect.width() / 2f;
+            float r2 = r1 * mRadiusRatio;
+
+            float[] startValues = new float[] {
+                    startRect.left, startRect.top, startRect.right, startRect.bottom, r1, r2};
+            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);
+            };
+        }
+    }
+
+    public static class Squircle extends PathShape {
+
+        /**
+         * Radio of radius to circle radius, based on the shape options defined in the config.
+         */
+        private final float mRadiusRatio;
+
+        public Squircle(float radiusRatio) {
+            mRadiusRatio = radiusRatio;
+        }
+
+        @Override
+        public void addToPath(Path p, float offsetX, float offsetY, float r) {
+            float cx = r + offsetX;
+            float cy = r + offsetY;
+            float control = r - r * mRadiusRatio;
+
+            p.moveTo(cx, cy - r);
+            addLeftCurve(cx, cy, r, control, p);
+            addRightCurve(cx, cy, r, control, p);
+            addLeftCurve(cx, cy, -r, -control, p);
+            addRightCurve(cx, cy, -r, -control, p);
+            p.close();
+        }
+
+        private void addLeftCurve(float cx, float cy, float r, float control, Path path) {
+            path.cubicTo(
+                    cx - control, cy - r,
+                    cx - r, cy - control,
+                    cx - r, cy);
+        }
+
+        private void addRightCurve(float cx, float cy, float r, float control, Path path) {
+            path.cubicTo(
+                    cx - r, cy + control,
+                    cx - control, cy + r,
+                    cx, cy + r);
+        }
+
+        @Override
+        protected AnimatorUpdateListener newUpdateListener(Rect startRect, Rect endRect,
+                float endR, Path outPath) {
+
+            float startCX = startRect.exactCenterX();
+            float startCY = startRect.exactCenterY();
+            float startR = startRect.width() / 2f;
+            float startControl = startR - startR * mRadiusRatio;
+            float startHShift = 0;
+            float startVShift = 0;
+
+            float endCX = endRect.exactCenterX();
+            float endCY = endRect.exactCenterY();
+            // Approximate corner circle using bezier curves
+            // http://spencermortensen.com/articles/bezier-circle/
+            float endControl = endR * 0.551915024494f;
+            float endHShift = endRect.width() / 2f - endR;
+            float endVShift = endRect.height() / 2f - endR;
+
+            return (anim) -> {
+                float progress = (Float) anim.getAnimatedValue();
+
+                float cx = (1 - progress) * startCX + progress * endCX;
+                float cy = (1 - progress) * startCY + progress * endCY;
+                float r = (1 - progress) * startR + progress * endR;
+                float control = (1 - progress) * startControl + progress * endControl;
+                float hShift = (1 - progress) * startHShift + progress * endHShift;
+                float vShift = (1 - progress) * startVShift + progress * endVShift;
+
+                outPath.moveTo(cx, cy - vShift - r);
+                outPath.rLineTo(-hShift, 0);
+
+                addLeftCurve(cx - hShift, cy - vShift, r, control, outPath);
+                outPath.rLineTo(0, vShift + vShift);
+
+                addRightCurve(cx - hShift, cy + vShift, r, control, outPath);
+                outPath.rLineTo(hShift + hShift, 0);
+
+                addLeftCurve(cx + hShift, cy + vShift, -r, -control, outPath);
+                outPath.rLineTo(0, -vShift - vShift);
+
+                addRightCurve(cx + hShift, cy - vShift, -r, -control, outPath);
+                outPath.close();
+            };
+        }
+    }
+
+    /**
+     * Initializes the shape which is closest to the {@link AdaptiveIconDrawable}
+     */
+    public static void init(Context context) {
+        if (!Utilities.ATLEAST_OREO) {
+            return;
+        }
+        pickBestShape(context);
+    }
+
+    private static IconShape getShapeDefinition(String type, float radius) {
+        switch (type) {
+            case "Circle":
+                return new Circle();
+            case "RoundedSquare":
+                return new RoundedSquare(radius);
+            case "TearDrop":
+                return new TearDrop(radius);
+            case "Squircle":
+                return new Squircle(radius);
+            default:
+                throw new IllegalArgumentException("Invalid shape type: " + type);
+        }
+    }
+
+    private static List<IconShape> getAllShapes(Context context) {
+        ArrayList<IconShape> result = new ArrayList<>();
+        try (XmlResourceParser parser = context.getResources().getXml(R.xml.folder_shapes)) {
+
+            // Find the root tag
+            int type;
+            while ((type = parser.next()) != XmlPullParser.END_TAG
+                    && type != XmlPullParser.END_DOCUMENT
+                    && !"shapes".equals(parser.getName()));
+
+            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) {
+
+                if (type == XmlPullParser.START_TAG) {
+                    AttributeSet attrs = Xml.asAttributeSet(parser);
+                    TypedArray a = context.obtainStyledAttributes(attrs, radiusAttr);
+                    IconShape shape = getShapeDefinition(parser.getName(), a.getFloat(0, 1));
+                    a.recycle();
+
+                    shape.mAttrs = Themes.createValueMap(context, attrs, keysToIgnore);
+                    result.add(shape);
+                }
+            }
+        } catch (IOException | XmlPullParserException e) {
+            throw new RuntimeException(e);
+        }
+        return result;
+    }
+
+    @TargetApi(Build.VERSION_CODES.O)
+    protected static void pickBestShape(Context context) {
+        // Pick any large size
+        final int size = 200;
+
+        Region full = new Region(0, 0, size, size);
+        Region iconR = new Region();
+        AdaptiveIconDrawable drawable = new AdaptiveIconDrawable(
+                new ColorDrawable(Color.BLACK), new ColorDrawable(Color.BLACK));
+        drawable.setBounds(0, 0, size, size);
+        iconR.setPath(drawable.getIconMask(), full);
+
+        Path shapePath = new Path();
+        Region shapeR = new Region();
+
+        // Find the shape with minimum area of divergent region.
+        int minArea = Integer.MAX_VALUE;
+        IconShape closestShape = null;
+        for (IconShape shape : getAllShapes(context)) {
+            shapePath.reset();
+            shape.addToPath(shapePath, 0, 0, size / 2f);
+            shapeR.setPath(shapePath, full);
+            shapeR.op(iconR, Op.XOR);
+
+            int area = GraphicsUtils.getArea(shapeR);
+            if (area < minArea) {
+                minArea = area;
+                closestShape = shape;
+            }
+        }
+
+        if (closestShape != null) {
+            sInstance = closestShape;
+        }
+
+        // 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/IconShapeOverride.java b/src/com/android/launcher3/graphics/IconShapeOverride.java
deleted file mode 100644
index 223243b..0000000
--- a/src/com/android/launcher3/graphics/IconShapeOverride.java
+++ /dev/null
@@ -1,213 +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 static com.android.launcher3.Utilities.getDevicePrefs;
-
-import android.annotation.TargetApi;
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.app.ProgressDialog;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.os.Build;
-import android.os.SystemClock;
-import android.preference.ListPreference;
-import android.preference.Preference;
-import android.preference.Preference.OnPreferenceChangeListener;
-import android.provider.Settings;
-import android.support.annotation.NonNull;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherModel;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.util.LooperExecutor;
-
-import java.lang.reflect.Field;
-
-/**
- * Utility class to override shape of {@link android.graphics.drawable.AdaptiveIconDrawable}.
- */
-@TargetApi(Build.VERSION_CODES.O)
-public class IconShapeOverride {
-
-    private static final String TAG = "IconShapeOverride";
-
-    public static final String KEY_PREFERENCE = "pref_override_icon_shape";
-
-    // Time to wait before killing the process this ensures that the progress bar is visible for
-    // sufficient time so that there is no flicker.
-    private static final long PROCESS_KILL_DELAY_MS = 1000;
-
-    private static final int RESTART_REQUEST_CODE = 42; // the answer to everything
-
-    public static boolean isSupported(Context context) {
-        if (!Utilities.ATLEAST_OREO) {
-            return false;
-        }
-        // Only supported when developer settings is enabled
-        if (Settings.Global.getInt(context.getContentResolver(),
-                Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 1) {
-            return false;
-        }
-
-        try {
-            if (getSystemResField().get(null) != Resources.getSystem()) {
-                // Our assumption that mSystem is the system resource is not true.
-                return false;
-            }
-        } catch (Exception e) {
-            // Ignore, not supported
-            return false;
-        }
-
-        return getConfigResId() != 0;
-    }
-
-    public static void apply(Context context) {
-        if (!Utilities.ATLEAST_OREO) {
-            return;
-        }
-        String path = getAppliedValue(context);
-        if (TextUtils.isEmpty(path)) {
-            return;
-        }
-        if (!isSupported(context)) {
-            return;
-        }
-
-        // magic
-        try {
-            Resources override =
-                    new ResourcesOverride(Resources.getSystem(), getConfigResId(), path);
-            getSystemResField().set(null, override);
-        } catch (Exception e) {
-            Log.e(TAG, "Unable to override icon shape", e);
-            // revert value.
-            getDevicePrefs(context).edit().remove(KEY_PREFERENCE).apply();
-        }
-    }
-
-    private static Field getSystemResField() throws Exception {
-        Field staticField = Resources.class.getDeclaredField("mSystem");
-        staticField.setAccessible(true);
-        return staticField;
-    }
-
-    private static int getConfigResId() {
-        return Resources.getSystem().getIdentifier("config_icon_mask", "string", "android");
-    }
-
-    private static String getAppliedValue(Context context) {
-        return getDevicePrefs(context).getString(KEY_PREFERENCE, "");
-    }
-
-    public static void handlePreferenceUi(ListPreference preference) {
-        Context context = preference.getContext();
-        preference.setValue(getAppliedValue(context));
-        preference.setOnPreferenceChangeListener(new PreferenceChangeHandler(context));
-    }
-
-    private static class ResourcesOverride extends Resources {
-
-        private final int mOverrideId;
-        private final String mOverrideValue;
-
-        @SuppressWarnings("deprecation")
-        public ResourcesOverride(Resources parent, int overrideId, String overrideValue) {
-            super(parent.getAssets(), parent.getDisplayMetrics(), parent.getConfiguration());
-            mOverrideId = overrideId;
-            mOverrideValue = overrideValue;
-        }
-
-        @NonNull
-        @Override
-        public String getString(int id) throws NotFoundException {
-            if (id == mOverrideId) {
-                return mOverrideValue;
-            }
-            return super.getString(id);
-        }
-    }
-
-    private static class PreferenceChangeHandler implements OnPreferenceChangeListener {
-
-        private final Context mContext;
-
-        private PreferenceChangeHandler(Context context) {
-            mContext = context;
-        }
-
-        @Override
-        public boolean onPreferenceChange(Preference preference, Object o) {
-            String newValue = (String) o;
-            if (!getAppliedValue(mContext).equals(newValue)) {
-                // Value has changed
-                ProgressDialog.show(mContext,
-                        null /* title */,
-                        mContext.getString(R.string.icon_shape_override_progress),
-                        true /* indeterminate */,
-                        false /* cancelable */);
-                new LooperExecutor(LauncherModel.getWorkerLooper()).execute(
-                        new OverrideApplyHandler(mContext, newValue));
-            }
-            return false;
-        }
-    }
-
-    private static class OverrideApplyHandler implements Runnable {
-
-        private final Context mContext;
-        private final String mValue;
-
-        private OverrideApplyHandler(Context context, String value) {
-            mContext = context;
-            mValue = value;
-        }
-
-        @Override
-        public void run() {
-            // Synchronously write the preference.
-            getDevicePrefs(mContext).edit().putString(KEY_PREFERENCE, mValue).commit();
-            // Clear the icon cache.
-            LauncherAppState.getInstance(mContext).getIconCache().clear();
-
-            // Wait for it
-            try {
-                Thread.sleep(PROCESS_KILL_DELAY_MS);
-            } catch (Exception e) {
-                Log.e(TAG, "Error waiting", e);
-            }
-
-            // Schedule an alarm before we kill ourself.
-            Intent homeIntent = new Intent(Intent.ACTION_MAIN)
-                    .addCategory(Intent.CATEGORY_HOME)
-                    .setPackage(mContext.getPackageName())
-                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            PendingIntent pi = PendingIntent.getActivity(mContext, RESTART_REQUEST_CODE,
-                    homeIntent, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
-            mContext.getSystemService(AlarmManager.class).setExact(
-                    AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + 50, pi);
-
-            // Kill process
-            android.os.Process.killProcess(android.os.Process.myPid());
-        }
-    }
-}
diff --git a/src/com/android/launcher3/graphics/LauncherIcons.java b/src/com/android/launcher3/graphics/LauncherIcons.java
deleted file mode 100644
index 333fe59..0000000
--- a/src/com/android/launcher3/graphics/LauncherIcons.java
+++ /dev/null
@@ -1,445 +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.graphics;
-
-import static android.graphics.Paint.DITHER_FLAG;
-import static android.graphics.Paint.FILTER_BITMAP_FLAG;
-
-import static com.android.launcher3.graphics.ShadowGenerator.BLUR_FACTOR;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.Intent.ShortcutIconResource;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.PaintFlagsDrawFilter;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.PaintDrawable;
-import android.os.Build;
-import android.os.Process;
-import android.os.UserHandle;
-import android.support.annotation.Nullable;
-
-import com.android.launcher3.AppInfo;
-import com.android.launcher3.FastBitmapDrawable;
-import com.android.launcher3.IconCache;
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.ItemInfoWithIcon;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.model.PackageItemInfo;
-import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
-import com.android.launcher3.util.Provider;
-import com.android.launcher3.util.Themes;
-
-/**
- * Helper methods for generating various launcher icons
- */
-public class LauncherIcons implements AutoCloseable {
-
-    private static final int DEFAULT_WRAPPER_BACKGROUND = Color.WHITE;
-
-    public static final Object sPoolSync = new Object();
-    private static LauncherIcons sPool;
-
-    /**
-     * Return a new Message instance from the global pool. Allows us to
-     * avoid allocating new objects in many cases.
-     */
-    public static LauncherIcons obtain(Context context) {
-        synchronized (sPoolSync) {
-            if (sPool != null) {
-                LauncherIcons m = sPool;
-                sPool = m.next;
-                m.next = null;
-                return m;
-            }
-        }
-        return new LauncherIcons(context);
-    }
-
-    /**
-     * Recycles a LauncherIcons that may be in-use.
-     */
-    public void recycle() {
-        synchronized (sPoolSync) {
-            // Clear any temporary state variables
-            mWrapperBackgroundColor = DEFAULT_WRAPPER_BACKGROUND;
-
-            next = sPool;
-            sPool = this;
-        }
-    }
-
-    @Override
-    public void close() {
-        recycle();
-    }
-
-    private final Rect mOldBounds = new Rect();
-    private final Context mContext;
-    private final Canvas mCanvas;
-    private final PackageManager mPm;
-
-    private final int mFillResIconDpi;
-    private final int mIconBitmapSize;
-
-    private IconNormalizer mNormalizer;
-    private ShadowGenerator mShadowGenerator;
-
-    private Drawable mWrapperIcon;
-    private int mWrapperBackgroundColor = DEFAULT_WRAPPER_BACKGROUND;
-
-    // sometimes we store linked lists of these things
-    private LauncherIcons next;
-
-    private LauncherIcons(Context context) {
-        mContext = context.getApplicationContext();
-        mPm = mContext.getPackageManager();
-
-        InvariantDeviceProfile idp = LauncherAppState.getIDP(mContext);
-        mFillResIconDpi = idp.fillResIconDpi;
-        mIconBitmapSize = idp.iconBitmapSize;
-
-        mCanvas = new Canvas();
-        mCanvas.setDrawFilter(new PaintFlagsDrawFilter(DITHER_FLAG, FILTER_BITMAP_FLAG));
-    }
-
-    public ShadowGenerator getShadowGenerator() {
-        if (mShadowGenerator == null) {
-            mShadowGenerator = new ShadowGenerator(mContext);
-        }
-        return mShadowGenerator;
-    }
-
-    public IconNormalizer getNormalizer() {
-        if (mNormalizer == null) {
-            mNormalizer = new IconNormalizer(mContext);
-        }
-        return mNormalizer;
-    }
-
-    /**
-     * Returns a bitmap suitable for the all apps view. If the package or the resource do not
-     * exist, it returns null.
-     */
-    public BitmapInfo createIconBitmap(ShortcutIconResource iconRes) {
-        try {
-            Resources resources = mPm.getResourcesForApplication(iconRes.packageName);
-            if (resources != null) {
-                final int id = resources.getIdentifier(iconRes.resourceName, null, null);
-                // do not stamp old legacy shortcuts as the app may have already forgotten about it
-                return createBadgedIconBitmap(
-                        resources.getDrawableForDensity(id, mFillResIconDpi),
-                        Process.myUserHandle() /* only available on primary user */,
-                        0 /* do not apply legacy treatment */);
-            }
-        } catch (Exception e) {
-            // Icon not found.
-        }
-        return null;
-    }
-
-    /**
-     * Returns a bitmap which is of the appropriate size to be displayed as an icon
-     */
-    public BitmapInfo createIconBitmap(Bitmap icon) {
-        if (mIconBitmapSize == icon.getWidth() && mIconBitmapSize == icon.getHeight()) {
-            return BitmapInfo.fromBitmap(icon);
-        }
-        return BitmapInfo.fromBitmap(
-                createIconBitmap(new BitmapDrawable(mContext.getResources(), icon), 1f));
-    }
-
-    /**
-     * Returns a bitmap suitable for displaying as an icon at various launcher UIs like all apps
-     * view or workspace. The icon is badged for {@param user}.
-     * The bitmap is also visually normalized with other icons.
-     */
-    public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user, int iconAppTargetSdk) {
-        return createBadgedIconBitmap(icon, user, iconAppTargetSdk, false, null);
-    }
-
-    public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user, int iconAppTargetSdk,
-            boolean isInstantApp) {
-        return createBadgedIconBitmap(icon, user, iconAppTargetSdk, isInstantApp, null);
-    }
-
-    /**
-     * Returns a bitmap suitable for displaying as an icon at various launcher UIs like all apps
-     * view or workspace. The icon is badged for {@param user}.
-     * The bitmap is also visually normalized with other icons.
-     */
-    public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user, int iconAppTargetSdk,
-            boolean isInstantApp, float [] scale) {
-        if (scale == null) {
-            scale = new float[1];
-        }
-        icon = normalizeAndWrapToAdaptiveIcon(icon, iconAppTargetSdk, null, scale);
-        Bitmap bitmap = createIconBitmap(icon, scale[0]);
-        if (Utilities.ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) {
-            mCanvas.setBitmap(bitmap);
-            getShadowGenerator().recreateIcon(Bitmap.createBitmap(bitmap), mCanvas);
-            mCanvas.setBitmap(null);
-        }
-
-        final Bitmap result;
-        if (user != null && !Process.myUserHandle().equals(user)) {
-            BitmapDrawable drawable = new FixedSizeBitmapDrawable(bitmap);
-            Drawable badged = mPm.getUserBadgedIcon(drawable, user);
-            if (badged instanceof BitmapDrawable) {
-                result = ((BitmapDrawable) badged).getBitmap();
-            } else {
-                result = createIconBitmap(badged, 1f);
-            }
-        } else if (isInstantApp) {
-            badgeWithDrawable(bitmap, mContext.getDrawable(R.drawable.ic_instant_app_badge));
-            result = bitmap;
-        } else {
-            result = bitmap;
-        }
-        return BitmapInfo.fromBitmap(result);
-    }
-
-    /**
-     * Creates a normalized bitmap suitable for the all apps view. The bitmap is also visually
-     * normalized with other icons and has enough spacing to add shadow.
-     */
-    public Bitmap createScaledBitmapWithoutShadow(Drawable icon, int iconAppTargetSdk) {
-        RectF iconBounds = new RectF();
-        float[] scale = new float[1];
-        icon = normalizeAndWrapToAdaptiveIcon(icon, iconAppTargetSdk, iconBounds, scale);
-        return createIconBitmap(icon,
-                Math.min(scale[0], ShadowGenerator.getScaleForBounds(iconBounds)));
-    }
-
-    /**
-     * Sets the background color used for wrapped adaptive icon
-     */
-    public void setWrapperBackgroundColor(int color) {
-        mWrapperBackgroundColor = (Color.alpha(color) < 255) ? DEFAULT_WRAPPER_BACKGROUND : color;
-    }
-
-    private Drawable normalizeAndWrapToAdaptiveIcon(Drawable icon, int iconAppTargetSdk,
-            RectF outIconBounds, float[] outScale) {
-        float scale = 1f;
-        if ((Utilities.ATLEAST_OREO && iconAppTargetSdk >= Build.VERSION_CODES.O) ||
-                Utilities.ATLEAST_P) {
-            boolean[] outShape = new boolean[1];
-            if (mWrapperIcon == null) {
-                mWrapperIcon = mContext.getDrawable(R.drawable.adaptive_icon_drawable_wrapper)
-                        .mutate();
-            }
-            AdaptiveIconDrawable dr = (AdaptiveIconDrawable) mWrapperIcon;
-            dr.setBounds(0, 0, 1, 1);
-            scale = getNormalizer().getScale(icon, outIconBounds, dr.getIconMask(), outShape);
-            if (Utilities.ATLEAST_OREO && !outShape[0] && !(icon instanceof AdaptiveIconDrawable)) {
-                FixedScaleDrawable fsd = ((FixedScaleDrawable) dr.getForeground());
-                fsd.setDrawable(icon);
-                fsd.setScale(scale);
-                icon = dr;
-                scale = getNormalizer().getScale(icon, outIconBounds, null, null);
-
-                ((ColorDrawable) dr.getBackground()).setColor(mWrapperBackgroundColor);
-            }
-        } else {
-            scale = getNormalizer().getScale(icon, outIconBounds, null, null);
-        }
-
-        outScale[0] = scale;
-        return icon;
-    }
-
-    /**
-     * Adds the {@param badge} on top of {@param target} using the badge dimensions.
-     */
-    public void badgeWithDrawable(Bitmap target, Drawable badge) {
-        mCanvas.setBitmap(target);
-        badgeWithDrawable(mCanvas, badge);
-        mCanvas.setBitmap(null);
-    }
-
-    /**
-     * Adds the {@param badge} on top of {@param target} using the badge dimensions.
-     */
-    private void badgeWithDrawable(Canvas target, Drawable badge) {
-        int badgeSize = mContext.getResources().getDimensionPixelSize(R.dimen.profile_badge_size);
-        badge.setBounds(mIconBitmapSize - badgeSize, mIconBitmapSize - badgeSize,
-                mIconBitmapSize, mIconBitmapSize);
-        badge.draw(target);
-    }
-
-    /**
-     * @param scale the scale to apply before drawing {@param icon} on the canvas
-     */
-    private Bitmap createIconBitmap(Drawable icon, float scale) {
-        int width = mIconBitmapSize;
-        int height = mIconBitmapSize;
-
-        if (icon instanceof PaintDrawable) {
-            PaintDrawable painter = (PaintDrawable) icon;
-            painter.setIntrinsicWidth(width);
-            painter.setIntrinsicHeight(height);
-        } else if (icon instanceof BitmapDrawable) {
-            // Ensure the bitmap has a density.
-            BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;
-            Bitmap bitmap = bitmapDrawable.getBitmap();
-            if (bitmap != null && bitmap.getDensity() == Bitmap.DENSITY_NONE) {
-                bitmapDrawable.setTargetDensity(mContext.getResources().getDisplayMetrics());
-            }
-        }
-
-        int sourceWidth = icon.getIntrinsicWidth();
-        int sourceHeight = icon.getIntrinsicHeight();
-        if (sourceWidth > 0 && sourceHeight > 0) {
-            // Scale the icon proportionally to the icon dimensions
-            final float ratio = (float) sourceWidth / sourceHeight;
-            if (sourceWidth > sourceHeight) {
-                height = (int) (width / ratio);
-            } else if (sourceHeight > sourceWidth) {
-                width = (int) (height * ratio);
-            }
-        }
-        // no intrinsic size --> use default size
-        int textureWidth = mIconBitmapSize;
-        int textureHeight = mIconBitmapSize;
-
-        Bitmap bitmap = Bitmap.createBitmap(textureWidth, textureHeight,
-                Bitmap.Config.ARGB_8888);
-        mCanvas.setBitmap(bitmap);
-
-        final int left = (textureWidth-width) / 2;
-        final int top = (textureHeight-height) / 2;
-
-        mOldBounds.set(icon.getBounds());
-        if (Utilities.ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) {
-            int offset = Math.max((int) Math.ceil(BLUR_FACTOR * textureWidth), Math.max(left, top));
-            int size = Math.max(width, height);
-            icon.setBounds(offset, offset, size - offset, size - offset);
-        } else {
-            icon.setBounds(left, top, left+width, top+height);
-        }
-        mCanvas.save();
-        mCanvas.scale(scale, scale, textureWidth / 2, textureHeight / 2);
-        icon.draw(mCanvas);
-        mCanvas.restore();
-        icon.setBounds(mOldBounds);
-        mCanvas.setBitmap(null);
-
-        return bitmap;
-    }
-
-    public BitmapInfo createShortcutIcon(ShortcutInfoCompat shortcutInfo) {
-        return createShortcutIcon(shortcutInfo, true /* badged */);
-    }
-
-    public BitmapInfo createShortcutIcon(ShortcutInfoCompat shortcutInfo, boolean badged) {
-        return createShortcutIcon(shortcutInfo, badged, null);
-    }
-
-    public BitmapInfo createShortcutIcon(ShortcutInfoCompat shortcutInfo,
-            boolean badged, @Nullable Provider<Bitmap> fallbackIconProvider) {
-        Drawable unbadgedDrawable = DeepShortcutManager.getInstance(mContext)
-                .getShortcutIconDrawable(shortcutInfo, mFillResIconDpi);
-        IconCache cache = LauncherAppState.getInstance(mContext).getIconCache();
-
-        final Bitmap unbadgedBitmap;
-        if (unbadgedDrawable != null) {
-            unbadgedBitmap = createScaledBitmapWithoutShadow(unbadgedDrawable, 0);
-        } else {
-            if (fallbackIconProvider != null) {
-                // Fallback icons are already badged and with appropriate shadow
-                Bitmap fullIcon = fallbackIconProvider.get();
-                if (fullIcon != null) {
-                    return createIconBitmap(fullIcon);
-                }
-            }
-            unbadgedBitmap = cache.getDefaultIcon(Process.myUserHandle()).icon;
-        }
-
-        BitmapInfo result = new BitmapInfo();
-        if (!badged) {
-            result.color = Themes.getColorAccent(mContext);
-            result.icon = unbadgedBitmap;
-            return result;
-        }
-
-        final Bitmap unbadgedfinal = unbadgedBitmap;
-        final ItemInfoWithIcon badge = getShortcutInfoBadge(shortcutInfo, cache);
-
-        result.color = badge.iconColor;
-        result.icon = BitmapRenderer.createHardwareBitmap(mIconBitmapSize, mIconBitmapSize, (c) -> {
-            getShadowGenerator().recreateIcon(unbadgedfinal, c);
-            badgeWithDrawable(c, new FastBitmapDrawable(badge));
-        });
-        return result;
-    }
-
-    public ItemInfoWithIcon getShortcutInfoBadge(ShortcutInfoCompat shortcutInfo, IconCache cache) {
-        ComponentName cn = shortcutInfo.getActivity();
-        String badgePkg = shortcutInfo.getBadgePackage(mContext);
-        boolean hasBadgePkgSet = !badgePkg.equals(shortcutInfo.getPackage());
-        if (cn != null && !hasBadgePkgSet) {
-            // Get the app info for the source activity.
-            AppInfo appInfo = new AppInfo();
-            appInfo.user = shortcutInfo.getUserHandle();
-            appInfo.componentName = cn;
-            appInfo.intent = new Intent(Intent.ACTION_MAIN)
-                    .addCategory(Intent.CATEGORY_LAUNCHER)
-                    .setComponent(cn);
-            cache.getTitleAndIcon(appInfo, false);
-            return appInfo;
-        } else {
-            PackageItemInfo pkgInfo = new PackageItemInfo(badgePkg);
-            cache.getTitleAndIconForApp(pkgInfo, false);
-            return pkgInfo;
-        }
-    }
-
-    /**
-     * An extension of {@link BitmapDrawable} which returns the bitmap pixel size as intrinsic size.
-     * This allows the badging to be done based on the action bitmap size rather than
-     * the scaled bitmap size.
-     */
-    private static class FixedSizeBitmapDrawable extends BitmapDrawable {
-
-        public FixedSizeBitmapDrawable(Bitmap bitmap) {
-            super(null, bitmap);
-        }
-
-        @Override
-        public int getIntrinsicHeight() {
-            return getBitmap().getWidth();
-        }
-
-        @Override
-        public int getIntrinsicWidth() {
-            return getBitmap().getWidth();
-        }
-    }
-}
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
new file mode 100644
index 0000000..d7b845b
--- /dev/null
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -0,0 +1,293 @@
+/*
+ * 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 android.view.View.MeasureSpec.EXACTLY;
+import static android.view.View.MeasureSpec.makeMeasureSpec;
+import static android.view.View.VISIBLE;
+
+import android.annotation.TargetApi;
+import android.app.Fragment;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Process;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextClock;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Hotseat;
+import com.android.launcher3.InsettableFrameLayout;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.R;
+import com.android.launcher3.WorkspaceItemInfo;
+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.icons.BaseIconFactory;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.BitmapRenderer;
+import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.views.BaseDragLayer;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * Utility class for generating the preview of Launcher for a given InvariantDeviceProfile.
+ * Steps:
+ *   1) Create a dummy icon info with just white icon
+ *   2) Inflate a strip down layout definition for Launcher
+ *   3) Place appropriate elements like icons and first-page qsb
+ *   4) Measure and draw the view on a canvas
+ */
+@TargetApi(Build.VERSION_CODES.O)
+public class LauncherPreviewRenderer implements Callable<Bitmap> {
+
+    private static final String TAG = "LauncherPreviewRenderer";
+
+    private final Handler mUiHandler;
+    private final Context mContext;
+    private final InvariantDeviceProfile mIdp;
+    private final DeviceProfile mDp;
+    private final Rect mInsets;
+
+    private final WorkspaceItemInfo mWorkspaceItemInfo;
+
+    public LauncherPreviewRenderer(Context context, InvariantDeviceProfile idp) {
+        mUiHandler = new Handler(Looper.getMainLooper());
+        mContext = context;
+        mIdp = idp;
+        mDp = idp.portraitProfile.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;
+        mDp.updateInsets(mInsets);
+
+        BaseIconFactory iconFactory =
+                new BaseIconFactory(context, mIdp.fillResIconDpi, mIdp.iconBitmapSize) { };
+        BitmapInfo iconInfo = iconFactory.createBadgedIconBitmap(new AdaptiveIconDrawable(
+                        new ColorDrawable(Color.WHITE), new ColorDrawable(Color.WHITE)),
+                Process.myUserHandle(),
+                Build.VERSION.SDK_INT);
+
+        mWorkspaceItemInfo = new WorkspaceItemInfo();
+        mWorkspaceItemInfo.applyFrom(iconInfo);
+        mWorkspaceItemInfo.intent = new Intent();
+        mWorkspaceItemInfo.contentDescription = mWorkspaceItemInfo.title =
+                context.getString(R.string.label_application);
+    }
+
+    @Override
+    public Bitmap call() {
+        return BitmapRenderer.createHardwareBitmap(mDp.widthPx, mDp.heightPx, c -> {
+
+            if (Looper.myLooper() == Looper.getMainLooper()) {
+                new MainThreadRenderer(mContext).renderScreenShot(c);
+            } else {
+                CountDownLatch latch = new CountDownLatch(1);
+                Utilities.postAsyncCallback(mUiHandler, () -> {
+                    new MainThreadRenderer(mContext).renderScreenShot(c);
+                    latch.countDown();
+                });
+
+                try {
+                    latch.await();
+                } catch (Exception e) {
+                    Log.e(TAG, "Error drawing on main thread", e);
+                }
+            }
+        });
+    }
+
+    private class MainThreadRenderer extends ContextThemeWrapper
+            implements ActivityContext, WorkspaceLayoutManager, LayoutInflater.Factory2 {
+
+        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 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) {
+
+                    @Override
+                    public Handler getHandler() {
+                        return mUiHandler;
+                    }
+                };
+            } else if (!"fragment".equals(name)) {
+                return null;
+            }
+
+            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);
+
+            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 View onCreateView(String name, Context context, AttributeSet attrs) {
+            return onCreateView(null, name, context, attrs);
+        }
+
+        @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 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();
+
+                for (int i = 0; i < count; i++) {
+                    dispatchVisibilityAggregated(vg.getChildAt(i), isVisible);
+                }
+            }
+        }
+
+        private void renderScreenShot(Canvas canvas) {
+            // 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);
+            }
+
+            // 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);
+
+            mRootView.draw(canvas);
+            dispatchVisibilityAggregated(mRootView, false);
+        }
+    }
+
+    private static void measureView(View view, int width, int height) {
+        view.measure(makeMeasureSpec(width, EXACTLY), makeMeasureSpec(height, EXACTLY));
+        view.layout(0, 0, width, height);
+    }
+}
diff --git a/src/com/android/launcher3/graphics/NinePatchDrawHelper.java b/src/com/android/launcher3/graphics/NinePatchDrawHelper.java
index fc20926..5872689 100644
--- a/src/com/android/launcher3/graphics/NinePatchDrawHelper.java
+++ b/src/com/android/launcher3/graphics/NinePatchDrawHelper.java
@@ -33,7 +33,9 @@
 
     private final Rect mSrc = new Rect();
     private final RectF mDst = new RectF();
-    public final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+    // 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
diff --git a/src/com/android/launcher3/graphics/PlaceHolderIconDrawable.java b/src/com/android/launcher3/graphics/PlaceHolderIconDrawable.java
new file mode 100644
index 0000000..23745cb
--- /dev/null
+++ b/src/com/android/launcher3/graphics/PlaceHolderIconDrawable.java
@@ -0,0 +1,64 @@
+/*
+ * 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 android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Path;
+import android.graphics.Rect;
+
+import com.android.launcher3.FastBitmapDrawable;
+import com.android.launcher3.ItemInfoWithIcon;
+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, Path progressPath, Context context) {
+        this(info.icon, info.color, progressPath, context);
+    }
+
+    public PlaceHolderIconDrawable(ItemInfoWithIcon info, Path progressPath, Context context) {
+        this(info.iconBitmap, info.iconColor, progressPath, context);
+    }
+
+    protected PlaceHolderIconDrawable(Bitmap b, int iconColor, Path progressPath, Context context) {
+        super(b, iconColor);
+
+        mProgressPath = progressPath;
+        mPaint.setColor(compositeColors(
+                Themes.getAttrColor(context, R.attr.loadingIconColor), iconColor));
+    }
+
+    @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 d3a7955..cc4c2ef 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -17,6 +17,8 @@
 
 package com.android.launcher3.graphics;
 
+import static com.android.launcher3.graphics.IconShape.DEFAULT_PATH_SIZE;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
@@ -55,8 +57,6 @@
                 }
             };
 
-    public static final int 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;
@@ -123,14 +123,14 @@
     protected void onBoundsChange(Rect bounds) {
         super.onBoundsChange(bounds);
         mTmpMatrix.setScale(
-                (bounds.width() - 2 * PROGRESS_WIDTH - 2 * PROGRESS_GAP) / PATH_SIZE,
-                (bounds.height() - 2 * PROGRESS_WIDTH - 2 * PROGRESS_GAP) / PATH_SIZE);
+                (bounds.width() - 2 * PROGRESS_WIDTH - 2 * PROGRESS_GAP) / DEFAULT_PATH_SIZE,
+                (bounds.height() - 2 * PROGRESS_WIDTH - 2 * PROGRESS_GAP) / DEFAULT_PATH_SIZE);
         mTmpMatrix.postTranslate(
                 bounds.left + PROGRESS_WIDTH + PROGRESS_GAP,
                 bounds.top + PROGRESS_WIDTH + PROGRESS_GAP);
 
         mProgressPath.transform(mTmpMatrix, mScaledTrackPath);
-        float scale = bounds.width() / PATH_SIZE;
+        float scale = bounds.width() / DEFAULT_PATH_SIZE;
         mProgressPaint.setStrokeWidth(PROGRESS_WIDTH * scale);
 
         mShadowBitmap = getShadowBitmap(bounds.width(), bounds.height(),
diff --git a/src/com/android/launcher3/graphics/RotationMode.java b/src/com/android/launcher3/graphics/RotationMode.java
new file mode 100644
index 0000000..b06305f
--- /dev/null
+++ b/src/com/android/launcher3/graphics/RotationMode.java
@@ -0,0 +1,48 @@
+/*
+ * 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 android.content.Context;
+import android.graphics.Rect;
+
+public abstract class RotationMode {
+
+    public static RotationMode NORMAL = new RotationMode(0) { };
+
+    public final float surfaceRotation;
+    public final boolean isTransposed;
+
+    public RotationMode(float surfaceRotation) {
+        this.surfaceRotation = surfaceRotation;
+        isTransposed = surfaceRotation != 0;
+    }
+
+    public final void mapRect(Rect rect, Rect out) {
+        mapRect(rect.left, rect.top, rect.right, rect.bottom, out);
+    }
+
+    public void mapRect(int left, int top, int right, int bottom, Rect out) {
+        out.set(left, top, right, bottom);
+    }
+
+    public void mapInsets(Context context, Rect insets, Rect out) {
+        out.set(insets);
+    }
+
+    public int toNaturalGravity(int absoluteGravity) {
+        return absoluteGravity;
+    }
+}
diff --git a/src/com/android/launcher3/graphics/ShadowDrawable.java b/src/com/android/launcher3/graphics/ShadowDrawable.java
index b40bf78..f10b972 100644
--- a/src/com/android/launcher3/graphics/ShadowDrawable.java
+++ b/src/com/android/launcher3/graphics/ShadowDrawable.java
@@ -32,7 +32,7 @@
 import android.util.AttributeSet;
 
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
+import com.android.launcher3.icons.BitmapRenderer;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -146,7 +146,7 @@
             d.draw(canvas);
         }
 
-        if (Utilities.ATLEAST_OREO) {
+        if (BitmapRenderer.USE_HARDWARE_BITMAP) {
             bitmap = bitmap.copy(Bitmap.Config.HARDWARE, false);
         }
         mState.mLastDrawnBitmap = bitmap;
diff --git a/src/com/android/launcher3/graphics/ShadowGenerator.java b/src/com/android/launcher3/graphics/ShadowGenerator.java
deleted file mode 100644
index 88da853..0000000
--- a/src/com/android/launcher3/graphics/ShadowGenerator.java
+++ /dev/null
@@ -1,170 +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.graphics;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.BlurMaskFilter;
-import android.graphics.BlurMaskFilter.Blur;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.RectF;
-import android.support.v4.graphics.ColorUtils;
-
-import com.android.launcher3.LauncherAppState;
-
-/**
- * Utility class to add shadows to bitmaps.
- */
-public class ShadowGenerator {
-
-    // Percent of actual icon size
-    private static final float HALF_DISTANCE = 0.5f;
-    public static final float BLUR_FACTOR = 0.5f/48;
-
-    // Percent of actual icon size
-    public static final float KEY_SHADOW_DISTANCE = 1f/48;
-    private static final int KEY_SHADOW_ALPHA = 61;
-
-    private static final int AMBIENT_SHADOW_ALPHA = 30;
-
-    private final int mIconSize;
-
-    private final Paint mBlurPaint;
-    private final Paint mDrawPaint;
-    private final BlurMaskFilter mDefaultBlurMaskFilter;
-
-    public ShadowGenerator(Context context) {
-        mIconSize = LauncherAppState.getIDP(context).iconBitmapSize;
-        mBlurPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
-        mDrawPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
-        mDefaultBlurMaskFilter = new BlurMaskFilter(mIconSize * BLUR_FACTOR, Blur.NORMAL);
-    }
-
-    public synchronized void recreateIcon(Bitmap icon, Canvas out) {
-        recreateIcon(icon, mDefaultBlurMaskFilter, AMBIENT_SHADOW_ALPHA, KEY_SHADOW_ALPHA, out);
-    }
-
-    public synchronized void recreateIcon(Bitmap icon, BlurMaskFilter blurMaskFilter,
-            int ambientAlpha, int keyAlpha, Canvas out) {
-        int[] offset = new int[2];
-        mBlurPaint.setMaskFilter(blurMaskFilter);
-        Bitmap shadow = icon.extractAlpha(mBlurPaint, offset);
-
-        // Draw ambient shadow
-        mDrawPaint.setAlpha(ambientAlpha);
-        out.drawBitmap(shadow, offset[0], offset[1], mDrawPaint);
-
-        // Draw key shadow
-        mDrawPaint.setAlpha(keyAlpha);
-        out.drawBitmap(shadow, offset[0], offset[1] + KEY_SHADOW_DISTANCE * mIconSize, mDrawPaint);
-
-        // Draw the icon
-        mDrawPaint.setAlpha(255);
-        out.drawBitmap(icon, 0, 0, mDrawPaint);
-    }
-
-    /**
-     * Returns the minimum amount by which an icon with {@param bounds} should be scaled
-     * so that the shadows do not get clipped.
-     */
-    public static float getScaleForBounds(RectF bounds) {
-        float scale = 1;
-
-        // For top, left & right, we need same space.
-        float minSide = Math.min(Math.min(bounds.left, bounds.right), bounds.top);
-        if (minSide < BLUR_FACTOR) {
-            scale = (HALF_DISTANCE - BLUR_FACTOR) / (HALF_DISTANCE - minSide);
-        }
-
-        float bottomSpace = BLUR_FACTOR + KEY_SHADOW_DISTANCE;
-        if (bounds.bottom < bottomSpace) {
-            scale = Math.min(scale, (HALF_DISTANCE - bottomSpace) / (HALF_DISTANCE - bounds.bottom));
-        }
-        return scale;
-    }
-
-    public static class Builder {
-
-        public final RectF bounds = new RectF();
-        public final int color;
-
-        public int ambientShadowAlpha = AMBIENT_SHADOW_ALPHA;
-
-        public float shadowBlur;
-
-        public float keyShadowDistance;
-        public int keyShadowAlpha = KEY_SHADOW_ALPHA;
-        public float radius;
-
-        public Builder(int color) {
-            this.color = color;
-        }
-
-        public Builder setupBlurForSize(int height) {
-            shadowBlur = height * 1f / 32;
-            keyShadowDistance = height * 1f / 16;
-            return this;
-        }
-
-        public Bitmap createPill(int width, int height) {
-            radius = height / 2f;
-
-            int centerX = Math.round(width / 2f + shadowBlur);
-            int centerY = Math.round(radius + shadowBlur + keyShadowDistance);
-            int center = Math.max(centerX, centerY);
-            bounds.set(0, 0, width, height);
-            bounds.offsetTo(center - width / 2f, center - height / 2f);
-
-            int size = center * 2;
-            Bitmap result = Bitmap.createBitmap(size, size, Config.ARGB_8888);
-            drawShadow(new Canvas(result));
-            return result;
-        }
-
-        public void drawShadow(Canvas c) {
-            Paint p = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
-            p.setColor(color);
-
-            // Key shadow
-            p.setShadowLayer(shadowBlur, 0, keyShadowDistance,
-                    ColorUtils.setAlphaComponent(Color.BLACK, keyShadowAlpha));
-            c.drawRoundRect(bounds, radius, radius, p);
-
-            // Ambient shadow
-            p.setShadowLayer(shadowBlur, 0, 0,
-                    ColorUtils.setAlphaComponent(Color.BLACK, ambientShadowAlpha));
-            c.drawRoundRect(bounds, radius, radius, p);
-
-            if (Color.alpha(color) < 255) {
-                // Clear any content inside the pill-rect for translucent fill.
-                p.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
-                p.clearShadowLayer();
-                p.setColor(Color.BLACK);
-                c.drawRoundRect(bounds, radius, radius, p);
-
-                p.setXfermode(null);
-                p.setColor(color);
-                c.drawRoundRect(bounds, radius, radius, p);
-            }
-        }
-    }
-}
diff --git a/src/com/android/launcher3/graphics/ShiftedBitmapDrawable.java b/src/com/android/launcher3/graphics/ShiftedBitmapDrawable.java
new file mode 100644
index 0000000..f8583b8
--- /dev/null
+++ b/src/com/android/launcher3/graphics/ShiftedBitmapDrawable.java
@@ -0,0 +1,105 @@
+/*
+ * 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 android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
+
+/**
+ * A simple drawable which draws a bitmap at a fixed position irrespective of the bounds
+ */
+public class ShiftedBitmapDrawable extends Drawable {
+
+    private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
+    private final Bitmap mBitmap;
+    private float mShiftX;
+    private float mShiftY;
+
+    private final ConstantState mConstantState;
+
+    public ShiftedBitmapDrawable(Bitmap bitmap, float shiftX, float shiftY) {
+        mBitmap = bitmap;
+        mShiftX = shiftX;
+        mShiftY = shiftY;
+
+        mConstantState = new MyConstantState(mBitmap, mShiftX, mShiftY);
+    }
+
+    public float getShiftX() {
+        return mShiftX;
+    }
+
+    public float getShiftY() {
+        return mShiftY;
+    }
+
+    public void setShiftX(float shiftX) {
+        mShiftX = shiftX;
+    }
+
+    public void setShiftY(float shiftY) {
+        mShiftY = shiftY;
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        canvas.drawBitmap(mBitmap, mShiftX, mShiftY, mPaint);
+    }
+
+    @Override
+    public void setAlpha(int i) { }
+
+    @Override
+    public void setColorFilter(ColorFilter colorFilter) {
+        mPaint.setColorFilter(colorFilter);
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.TRANSLUCENT;
+    }
+
+    @Override
+    public ConstantState getConstantState() {
+        return mConstantState;
+    }
+
+    private static class MyConstantState extends ConstantState {
+        private final Bitmap mBitmap;
+        private float mShiftX;
+        private float mShiftY;
+
+        MyConstantState(Bitmap bitmap, float shiftX, float shiftY) {
+            mBitmap = bitmap;
+            mShiftX = shiftX;
+            mShiftY = shiftY;
+        }
+
+        @Override
+        public Drawable newDrawable() {
+            return new ShiftedBitmapDrawable(mBitmap, mShiftX, mShiftY);
+        }
+
+        @Override
+        public int getChangingConfigurations() {
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/graphics/TintedDrawableSpan.java b/src/com/android/launcher3/graphics/TintedDrawableSpan.java
index d719575..0bfc435 100644
--- a/src/com/android/launcher3/graphics/TintedDrawableSpan.java
+++ b/src/com/android/launcher3/graphics/TintedDrawableSpan.java
@@ -32,7 +32,7 @@
 
     public TintedDrawableSpan(Context context, int resourceId) {
         super(ALIGN_BOTTOM);
-        mDrawable = context.getDrawable(resourceId);
+        mDrawable = context.getDrawable(resourceId).mutate();
         mOldTint = 0;
         mDrawable.setTint(0);
     }
diff --git a/src/com/android/launcher3/graphics/TriangleShape.java b/src/com/android/launcher3/graphics/TriangleShape.java
index cce4e3c..2c15725 100644
--- a/src/com/android/launcher3/graphics/TriangleShape.java
+++ b/src/com/android/launcher3/graphics/TriangleShape.java
@@ -19,7 +19,8 @@
 import android.graphics.Outline;
 import android.graphics.Path;
 import android.graphics.drawable.shapes.PathShape;
-import android.support.annotation.NonNull;
+
+import androidx.annotation.NonNull;
 
 /**
  * Wrapper around {@link android.graphics.drawable.shapes.PathShape}
diff --git a/src/com/android/launcher3/graphics/ViewScrim.java b/src/com/android/launcher3/graphics/ViewScrim.java
deleted file mode 100644
index e1727e0..0000000
--- a/src/com/android/launcher3/graphics/ViewScrim.java
+++ /dev/null
@@ -1,77 +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 android.graphics.Canvas;
-import android.util.Property;
-import android.view.View;
-import android.view.ViewParent;
-
-import com.android.launcher3.R;
-
-/**
- * A utility class that can be used to draw a scrim behind a view
- */
-public abstract class ViewScrim<T extends View> {
-
-    public static Property<ViewScrim, Float> PROGRESS =
-            new Property<ViewScrim, Float>(Float.TYPE, "progress") {
-                @Override
-                public Float get(ViewScrim viewScrim) {
-                    return viewScrim.mProgress;
-                }
-
-                @Override
-                public void set(ViewScrim object, Float value) {
-                    object.setProgress(value);
-                }
-            };
-
-    protected final T mView;
-    protected float mProgress = 0;
-
-    public ViewScrim(T view) {
-        mView = view;
-    }
-
-    public void attach() {
-        mView.setTag(R.id.view_scrim, this);
-    }
-
-    public void setProgress(float progress) {
-        if (mProgress != progress) {
-            mProgress = progress;
-            onProgressChanged();
-            invalidate();
-        }
-    }
-
-    public abstract void draw(Canvas canvas, int width, int height);
-
-    protected void onProgressChanged() { }
-
-    public void invalidate() {
-        ViewParent parent = mView.getParent();
-        if (parent != null) {
-            ((View) parent).invalidate();
-        }
-    }
-
-    public static ViewScrim get(View view) {
-        return (ViewScrim) view.getTag(R.id.view_scrim);
-    }
-}
diff --git a/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java b/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java
index bc4a06d..c0aa75f 100644
--- a/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java
+++ b/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java
@@ -19,6 +19,8 @@
 import static android.content.Intent.ACTION_SCREEN_OFF;
 import static android.content.Intent.ACTION_USER_PRESENT;
 
+import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
+
 import android.animation.ObjectAnimator;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -34,7 +36,6 @@
 import android.graphics.Region;
 import android.graphics.Shader;
 import android.graphics.drawable.Drawable;
-import android.support.v4.graphics.ColorUtils;
 import android.util.DisplayMetrics;
 import android.util.Property;
 import android.view.View;
@@ -42,11 +43,13 @@
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
+import com.android.launcher3.ResourceUtils;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.uioverrides.WallpaperColorInfo;
 import com.android.launcher3.util.Themes;
 
+import androidx.core.graphics.ColorUtils;
+
 /**
  * View scrim which draws behind hotseat and workspace
  */
@@ -120,7 +123,6 @@
 
     private Workspace mWorkspace;
 
-    private final boolean mHasSysUiScrim;
     private boolean mDrawTopScrim, mDrawBottomScrim;
 
     private final RectF mFinalMaskRect = new RectF();
@@ -146,17 +148,11 @@
         mLauncher = Launcher.getLauncher(view.getContext());
         mWallpaperColorInfo = WallpaperColorInfo.getInstance(mLauncher);
 
-        mMaskHeight = Utilities.pxFromDp(ALPHA_MASK_BITMAP_DP,
+        mMaskHeight = ResourceUtils.pxFromDp(ALPHA_MASK_BITMAP_DP,
                 view.getResources().getDisplayMetrics());
-
-        mHasSysUiScrim = !mWallpaperColorInfo.supportsDarkText();
-        if (mHasSysUiScrim) {
-            mTopScrim = Themes.getAttrDrawable(view.getContext(), R.attr.workspaceStatusBarScrim);
-            mBottomMask = createDitheredAlphaMask();
-        } else {
-            mTopScrim = null;
-            mBottomMask = null;
-        }
+        mTopScrim = Themes.getAttrDrawable(view.getContext(), R.attr.workspaceStatusBarScrim);
+        mBottomMask = mTopScrim == null ? null : createDitheredAlphaMask();
+        mHideSysUiScrim = mTopScrim == null;
 
         view.addOnAttachStateChangeListener(this);
         onExtractedColorsChanged(mWallpaperColorInfo);
@@ -173,18 +169,18 @@
             mWorkspace.computeScrollWithoutInvalidation();
             CellLayout currCellLayout = mWorkspace.getCurrentDragOverlappingLayout();
             canvas.save();
-            if (currCellLayout != null && currCellLayout != mLauncher.getHotseat().getLayout()) {
+            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);
             }
 
-            canvas.drawColor(ColorUtils.setAlphaComponent(mFullScrimColor, mScrimAlpha));
+            canvas.drawColor(setColorAlphaBound(mFullScrimColor, mScrimAlpha));
             canvas.restore();
         }
 
-        if (!mHideSysUiScrim && mHasSysUiScrim) {
+        if (!mHideSysUiScrim) {
             if (mSysUiProgress <= 0) {
                 mAnimateScrimOnNextDraw = false;
                 return;
@@ -212,8 +208,9 @@
     }
 
     public void onInsetsChanged(Rect insets) {
-        mDrawTopScrim = insets.top > 0;
-        mDrawBottomScrim = !mLauncher.getDeviceProfile().isVerticalBarLayout();
+        mDrawTopScrim = mTopScrim != null && insets.top > 0;
+        mDrawBottomScrim = mBottomMask != null &&
+                !mLauncher.getDeviceProfile().isVerticalBarLayout();
     }
 
     private void setScrimProgress(float progress) {
@@ -229,7 +226,7 @@
         mWallpaperColorInfo.addOnChangeListener(this);
         onExtractedColorsChanged(mWallpaperColorInfo);
 
-        if (mHasSysUiScrim) {
+        if (mTopScrim != null) {
             IntentFilter filter = new IntentFilter(ACTION_SCREEN_OFF);
             filter.addAction(ACTION_USER_PRESENT); // When the device wakes up + keyguard is gone
             mRoot.getContext().registerReceiver(mReceiver, filter);
@@ -239,7 +236,7 @@
     @Override
     public void onViewDetachedFromWindow(View view) {
         mWallpaperColorInfo.removeOnChangeListener(this);
-        if (mHasSysUiScrim) {
+        if (mTopScrim != null) {
             mRoot.getContext().unregisterReceiver(mReceiver);
         }
     }
@@ -258,14 +255,14 @@
     }
 
     public void setSize(int w, int h) {
-        if (mHasSysUiScrim) {
+        if (mTopScrim != null) {
             mTopScrim.setBounds(0, 0, w, h);
             mFinalMaskRect.set(0, h - mMaskHeight, w, h);
         }
     }
 
     public void hideSysUiScrim(boolean hideSysUiScrim) {
-        mHideSysUiScrim = hideSysUiScrim;
+        mHideSysUiScrim = hideSysUiScrim || (mTopScrim == null);
         if (!hideSysUiScrim) {
             mAnimateScrimOnNextDraw = true;
         }
@@ -280,18 +277,18 @@
     }
 
     private void reapplySysUiAlpha() {
-        if (mHasSysUiScrim) {
-            reapplySysUiAlphaNoInvalidate();
-            if (!mHideSysUiScrim) {
-                invalidate();
-            }
+        reapplySysUiAlphaNoInvalidate();
+        if (!mHideSysUiScrim) {
+            invalidate();
         }
     }
 
     private void reapplySysUiAlphaNoInvalidate() {
         float factor = mSysUiProgress * mSysUiAnimMultiplier;
         mBottomMaskPaint.setAlpha(Math.round(MAX_HOTSEAT_SCRIM_ALPHA * factor));
-        mTopScrim.setAlpha(Math.round(255 * factor));
+        if (mTopScrim != null) {
+            mTopScrim.setAlpha(Math.round(255 * factor));
+        }
     }
 
     public void invalidate() {
@@ -300,15 +297,15 @@
 
     public Bitmap createDitheredAlphaMask() {
         DisplayMetrics dm = mLauncher.getResources().getDisplayMetrics();
-        int width = Utilities.pxFromDp(ALPHA_MASK_WIDTH_DP, dm);
-        int gradientHeight = Utilities.pxFromDp(ALPHA_MASK_HEIGHT_DP, dm);
+        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);
         Canvas c = new Canvas(dst);
         Paint paint = new Paint(Paint.DITHER_FLAG);
         LinearGradient lg = new LinearGradient(0, 0, 0, gradientHeight,
                 new int[]{
                         0x00FFFFFF,
-                        ColorUtils.setAlphaComponent(Color.WHITE, (int) (0xFF * 0.95)),
+                        setColorAlphaBound(Color.WHITE, (int) (0xFF * 0.95)),
                         0xFFFFFFFF},
                 new float[]{0f, 0.8f, 1f},
                 Shader.TileMode.CLAMP);
diff --git a/src/com/android/launcher3/icons/ComponentWithLabel.java b/src/com/android/launcher3/icons/ComponentWithLabel.java
new file mode 100644
index 0000000..46b5002
--- /dev/null
+++ b/src/com/android/launcher3/icons/ComponentWithLabel.java
@@ -0,0 +1,64 @@
+/*
+ * 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.icons;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.UserHandle;
+
+import com.android.launcher3.icons.cache.CachingLogic;
+
+public interface ComponentWithLabel {
+
+    ComponentName getComponent();
+
+    UserHandle getUser();
+
+    CharSequence getLabel(PackageManager pm);
+
+
+    class ComponentCachingLogic implements CachingLogic<ComponentWithLabel> {
+
+        private final PackageManager mPackageManager;
+
+        public ComponentCachingLogic(Context context) {
+            mPackageManager = context.getPackageManager();
+        }
+
+        @Override
+        public ComponentName getComponent(ComponentWithLabel object) {
+            return object.getComponent();
+        }
+
+        @Override
+        public UserHandle getUser(ComponentWithLabel object) {
+            return object.getUser();
+        }
+
+        @Override
+        public CharSequence getLabel(ComponentWithLabel object) {
+            return object.getLabel(mPackageManager);
+        }
+
+        @Override
+        public void loadIcon(Context context,
+                ComponentWithLabel object, BitmapInfo target) {
+            // Do not load icon.
+            target.icon = BitmapInfo.LOW_RES_ICON;
+        }
+    }
+}
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
new file mode 100644
index 0000000..648445e
--- /dev/null
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -0,0 +1,256 @@
+/*
+ * 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.icons;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.LauncherActivityInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Process;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.IconProvider;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.ItemInfoWithIcon;
+import com.android.launcher3.LauncherFiles;
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic;
+import com.android.launcher3.icons.cache.BaseIconCache;
+import com.android.launcher3.icons.cache.CachingLogic;
+import com.android.launcher3.icons.cache.HandlerRunnable;
+import com.android.launcher3.model.PackageItemInfo;
+import com.android.launcher3.util.InstantAppResolver;
+import com.android.launcher3.util.Preconditions;
+
+import java.util.function.Supplier;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Cache of application icons.  Icons can be made from any thread.
+ */
+public class IconCache extends BaseIconCache {
+
+    private static final String TAG = "Launcher.IconCache";
+
+    private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
+
+    private final CachingLogic<ComponentWithLabel> mComponentWithLabelCachingLogic;
+    private final CachingLogic<LauncherActivityInfo> mLauncherActivityInfoCachingLogic;
+
+    private final LauncherAppsCompat mLauncherApps;
+    private final UserManagerCompat mUserManager;
+    private final InstantAppResolver mInstantAppResolver;
+    private final IconProvider mIconProvider;
+
+    private int mPendingIconRequestCount = 0;
+
+    public IconCache(Context context, InvariantDeviceProfile inv) {
+        super(context, LauncherFiles.APP_ICONS_DB, LauncherModel.getWorkerLooper(),
+                inv.fillResIconDpi, inv.iconBitmapSize, true /* inMemoryCache */);
+        mComponentWithLabelCachingLogic = new ComponentCachingLogic(context);
+        mLauncherActivityInfoCachingLogic = new LauncherActivtiyCachingLogic(this);
+        mLauncherApps = LauncherAppsCompat.getInstance(mContext);
+        mUserManager = UserManagerCompat.getInstance(mContext);
+        mInstantAppResolver = InstantAppResolver.newInstance(mContext);
+        mIconProvider = IconProvider.newInstance(context);
+    }
+
+    @Override
+    protected long getSerialNumberForUser(UserHandle user) {
+        return mUserManager.getSerialNumberForUser(user);
+    }
+
+    @Override
+    protected boolean isInstantApp(ApplicationInfo info) {
+        return mInstantAppResolver.isInstantApp(info);
+    }
+
+    @Override
+    protected BaseIconFactory getIconFactory() {
+        return LauncherIcons.obtain(mContext);
+    }
+
+    /**
+     * Updates the entries related to the given package in memory and persistent DB.
+     */
+    public synchronized void updateIconsForPkg(String packageName, UserHandle user) {
+        removeIconsForPkg(packageName, user);
+        try {
+            PackageInfo info = mPackageManager.getPackageInfo(packageName,
+                    PackageManager.GET_UNINSTALLED_PACKAGES);
+            long userSerial = mUserManager.getSerialNumberForUser(user);
+            for (LauncherActivityInfo app : mLauncherApps.getActivityList(packageName, user)) {
+                addIconToDBAndMemCache(app, mLauncherActivityInfoCachingLogic, info, userSerial,
+                        false /*replace existing*/);
+            }
+        } catch (NameNotFoundException e) {
+            Log.d(TAG, "Package not found", e);
+        }
+    }
+
+    /**
+     * 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,
+            final ItemInfoWithIcon info) {
+        Preconditions.assertUIThread();
+        if (mPendingIconRequestCount <= 0) {
+            LauncherModel.setWorkerPriority(Process.THREAD_PRIORITY_FOREGROUND);
+        }
+        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);
+                }
+                mMainThreadExecutor.execute(() -> {
+                    caller.reapplyItemInfo(info);
+                    onEnd();
+                });
+            }
+        };
+        Utilities.postAsyncCallback(mWorkerHandler, request);
+        return request;
+    }
+
+    private void onIconRequestEnd() {
+        mPendingIconRequestCount --;
+        if (mPendingIconRequestCount <= 0) {
+            LauncherModel.setWorkerPriority(Process.THREAD_PRIORITY_BACKGROUND);
+        }
+    }
+
+    /**
+     * Updates {@param application} only if a valid entry is found.
+     */
+    public synchronized void updateTitleAndIcon(AppInfo application) {
+        CacheEntry entry = cacheLocked(application.componentName,
+                application.user, () -> null, mLauncherActivityInfoCachingLogic,
+                false, application.usingLowResIcon());
+        if (entry.icon != null && !isDefaultIcon(entry.icon, application.user)) {
+            applyCacheEntry(entry, application);
+        }
+    }
+
+    /**
+     * Fill in {@param info} with the icon and label for {@param activityInfo}
+     */
+    public synchronized void getTitleAndIcon(ItemInfoWithIcon info,
+            LauncherActivityInfo activityInfo, boolean useLowResIcon) {
+        // If we already have activity info, no need to use package icon
+        getTitleAndIcon(info, () -> activityInfo, false, useLowResIcon);
+    }
+
+    /**
+     * Fill in {@param info} with the icon and label. If the
+     * corresponding activity is not found, it reverts to the package icon.
+     */
+    public synchronized void getTitleAndIcon(ItemInfoWithIcon info, boolean useLowResIcon) {
+        // null info means not installed, but if we have a component from the intent then
+        // we should still look in the cache for restored app icons.
+        if (info.getTargetComponent() == null) {
+            info.applyFrom(getDefaultIcon(info.user));
+            info.title = "";
+            info.contentDescription = "";
+        } else {
+            Intent intent = info.getIntent();
+            getTitleAndIcon(info, () -> mLauncherApps.resolveActivity(intent, info.user),
+                    true, useLowResIcon);
+        }
+    }
+
+    public synchronized String getTitleNoCache(ComponentWithLabel info) {
+        CacheEntry entry = cacheLocked(info.getComponent(), info.getUser(), () -> info,
+                mComponentWithLabelCachingLogic, false /* usePackageIcon */,
+                true /* useLowResIcon */, false /* addToMemCache */);
+        return Utilities.trim(entry.title);
+    }
+
+    /**
+     * Fill in {@param mWorkspaceItemInfo} with the icon and label for {@param info}
+     */
+    private 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);
+        applyCacheEntry(entry, infoInOut);
+    }
+
+
+    /**
+     * Fill in {@param infoInOut} with the corresponding icon and label.
+     */
+    public synchronized void getTitleAndIconForApp(
+            PackageItemInfo infoInOut, boolean useLowResIcon) {
+        CacheEntry entry = getEntryForPackageLocked(
+                infoInOut.packageName, infoInOut.user, useLowResIcon);
+        applyCacheEntry(entry, infoInOut);
+    }
+
+    protected void applyCacheEntry(CacheEntry entry, ItemInfoWithIcon info) {
+        info.title = Utilities.trim(entry.title);
+        info.contentDescription = entry.contentDescription;
+        info.applyFrom((entry.icon == null) ? getDefaultIcon(info.user) : entry);
+    }
+
+    public Drawable getFullResIcon(LauncherActivityInfo info) {
+        return getFullResIcon(info, true);
+    }
+
+    public Drawable getFullResIcon(LauncherActivityInfo info, boolean flattenDrawable) {
+        return mIconProvider.getIcon(info, mIconDpi, flattenDrawable);
+    }
+
+    @Override
+    protected String getIconSystemState(String packageName) {
+        return mIconProvider.getSystemStateForPackage(mSystemState, packageName);
+    }
+
+    public static abstract class IconLoadRequest extends HandlerRunnable {
+        IconLoadRequest(Handler handler, Runnable endRunnable) {
+            super(handler, endRunnable);
+        }
+    }
+
+    /**
+     * Interface for receiving itemInfo with high-res icon.
+     */
+    public interface ItemInfoUpdateReceiver {
+
+        void reapplyItemInfo(ItemInfoWithIcon info);
+    }
+}
diff --git a/src/com/android/launcher3/icons/LauncherActivtiyCachingLogic.java b/src/com/android/launcher3/icons/LauncherActivtiyCachingLogic.java
new file mode 100644
index 0000000..7c99633
--- /dev/null
+++ b/src/com/android/launcher3/icons/LauncherActivtiyCachingLogic.java
@@ -0,0 +1,56 @@
+/*
+ * 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.icons;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.LauncherActivityInfo;
+import android.os.UserHandle;
+
+import com.android.launcher3.icons.cache.CachingLogic;
+
+public class LauncherActivtiyCachingLogic implements CachingLogic<LauncherActivityInfo> {
+
+    private final IconCache mCache;
+
+    public LauncherActivtiyCachingLogic(IconCache cache) {
+        mCache = cache;
+    }
+
+    @Override
+    public ComponentName getComponent(LauncherActivityInfo object) {
+        return object.getComponentName();
+    }
+
+    @Override
+    public UserHandle getUser(LauncherActivityInfo object) {
+        return object.getUser();
+    }
+
+    @Override
+    public CharSequence getLabel(LauncherActivityInfo object) {
+        return object.getLabel();
+    }
+
+    @Override
+    public void loadIcon(Context context, LauncherActivityInfo object,
+            BitmapInfo target) {
+        LauncherIcons li = LauncherIcons.obtain(context);
+        li.createBadgedIconBitmap(mCache.getFullResIcon(object),
+                object.getUser(), object.getApplicationInfo().targetSdkVersion).applyTo(target);
+        li.recycle();
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/icons/LauncherIcons.java b/src/com/android/launcher3/icons/LauncherIcons.java
new file mode 100644
index 0000000..7632408
--- /dev/null
+++ b/src/com/android/launcher3/icons/LauncherIcons.java
@@ -0,0 +1,198 @@
+/*
+ * 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.icons;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ShortcutInfo;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.os.Process;
+
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.FastBitmapDrawable;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.ItemInfoWithIcon;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.R;
+import com.android.launcher3.graphics.IconShape;
+import com.android.launcher3.model.PackageItemInfo;
+import com.android.launcher3.shortcuts.DeepShortcutManager;
+import com.android.launcher3.util.Themes;
+
+import java.util.function.Supplier;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Wrapper class to provide access to {@link BaseIconFactory} and also to provide pool of this class
+ * that are threadsafe.
+ */
+public class LauncherIcons extends BaseIconFactory implements AutoCloseable {
+
+    private static final String EXTRA_BADGEPKG = "badge_package";
+
+    private static final Object sPoolSync = new Object();
+    private static LauncherIcons sPool;
+    private static int sPoolId = 0;
+
+    public static LauncherIcons obtain(Context context) {
+        return obtain(context, IconShape.getShape().enableShapeDetection());
+    }
+
+    /**
+     * Return a new Message instance from the global pool. Allows us to
+     * avoid allocating new objects in many cases.
+     */
+    public static LauncherIcons obtain(Context context, boolean shapeDetection) {
+        int poolId;
+        synchronized (sPoolSync) {
+            if (sPool != null) {
+                LauncherIcons m = sPool;
+                sPool = m.next;
+                m.next = null;
+                return m;
+            }
+            poolId = sPoolId;
+        }
+
+        InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
+        return new LauncherIcons(context, idp.fillResIconDpi, idp.iconBitmapSize, poolId,
+                shapeDetection);
+    }
+
+    public static void clearPool() {
+        synchronized (sPoolSync) {
+            sPool = null;
+            sPoolId++;
+        }
+    }
+
+    private final int mPoolId;
+
+    private LauncherIcons next;
+
+    private LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize, int poolId,
+            boolean shapeDetection) {
+        super(context, fillResIconDpi, iconBitmapSize, shapeDetection);
+        mPoolId = poolId;
+    }
+
+    /**
+     * Recycles a LauncherIcons that may be in-use.
+     */
+    public void recycle() {
+        synchronized (sPoolSync) {
+            if (sPoolId != mPoolId) {
+                return;
+            }
+            // Clear any temporary state variables
+            clear();
+
+            next = sPool;
+            sPool = this;
+        }
+    }
+
+    @Override
+    public void close() {
+        recycle();
+    }
+
+    // below methods should also migrate to BaseIconFactory
+
+    public BitmapInfo createShortcutIcon(ShortcutInfo shortcutInfo) {
+        return createShortcutIcon(shortcutInfo, true /* badged */);
+    }
+
+    public BitmapInfo createShortcutIcon(ShortcutInfo shortcutInfo, boolean badged) {
+        return createShortcutIcon(shortcutInfo, badged, null);
+    }
+
+    public BitmapInfo createShortcutIcon(ShortcutInfo shortcutInfo,
+            boolean badged, @Nullable Supplier<ItemInfoWithIcon> fallbackIconProvider) {
+        Drawable unbadgedDrawable = DeepShortcutManager.getInstance(mContext)
+                .getShortcutIconDrawable(shortcutInfo, mFillResIconDpi);
+        IconCache cache = LauncherAppState.getInstance(mContext).getIconCache();
+
+        final Bitmap unbadgedBitmap;
+        if (unbadgedDrawable != null) {
+            unbadgedBitmap = createScaledBitmapWithoutShadow(unbadgedDrawable, 0);
+        } else {
+            if (fallbackIconProvider != null) {
+                // Fallback icons are already badged and with appropriate shadow
+                ItemInfoWithIcon fullIcon = fallbackIconProvider.get();
+                if (fullIcon != null && fullIcon.iconBitmap != null) {
+                    BitmapInfo result = new BitmapInfo();
+                    result.icon = fullIcon.iconBitmap;
+                    result.color = fullIcon.iconColor;
+                    return result;
+                }
+            }
+            unbadgedBitmap = cache.getDefaultIcon(Process.myUserHandle()).icon;
+        }
+
+        BitmapInfo result = new BitmapInfo();
+        if (!badged) {
+            result.color = Themes.getColorAccent(mContext);
+            result.icon = unbadgedBitmap;
+            return result;
+        }
+
+        final Bitmap unbadgedfinal = unbadgedBitmap;
+        final ItemInfoWithIcon badge = getShortcutInfoBadge(shortcutInfo, cache);
+
+        result.color = badge.iconColor;
+        result.icon = BitmapRenderer.createHardwareBitmap(mIconBitmapSize, mIconBitmapSize, (c) -> {
+            getShadowGenerator().recreateIcon(unbadgedfinal, c);
+            badgeWithDrawable(c, new FastBitmapDrawable(badge));
+        });
+        return result;
+    }
+
+    public ItemInfoWithIcon getShortcutInfoBadge(ShortcutInfo shortcutInfo, IconCache cache) {
+        ComponentName cn = shortcutInfo.getActivity();
+        String badgePkg = getBadgePackage(shortcutInfo);
+        boolean hasBadgePkgSet = !badgePkg.equals(shortcutInfo.getPackage());
+        if (cn != null && !hasBadgePkgSet) {
+            // Get the app info for the source activity.
+            AppInfo appInfo = new AppInfo();
+            appInfo.user = shortcutInfo.getUserHandle();
+            appInfo.componentName = cn;
+            appInfo.intent = new Intent(Intent.ACTION_MAIN)
+                    .addCategory(Intent.CATEGORY_LAUNCHER)
+                    .setComponent(cn);
+            cache.getTitleAndIcon(appInfo, false);
+            return appInfo;
+        } else {
+            PackageItemInfo pkgInfo = new PackageItemInfo(badgePkg);
+            cache.getTitleAndIconForApp(pkgInfo, false);
+            return pkgInfo;
+        }
+    }
+
+    private String getBadgePackage(ShortcutInfo si) {
+        String whitelistedPkg = mContext.getString(R.string.shortcutinfo_badgepkg_whitelist);
+        if (whitelistedPkg.equals(si.getPackage())
+                && si.getExtras() != null
+                && si.getExtras().containsKey(EXTRA_BADGEPKG)) {
+            return si.getExtras().getString(EXTRA_BADGEPKG);
+        }
+        return si.getPackage();
+    }
+}
diff --git a/src/com/android/launcher3/keyboard/FocusedItemDecorator.java b/src/com/android/launcher3/keyboard/FocusedItemDecorator.java
index 05ae406..2476a6f 100644
--- a/src/com/android/launcher3/keyboard/FocusedItemDecorator.java
+++ b/src/com/android/launcher3/keyboard/FocusedItemDecorator.java
@@ -17,14 +17,15 @@
 package com.android.launcher3.keyboard;
 
 import android.graphics.Canvas;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.RecyclerView.ItemDecoration;
-import android.support.v7.widget.RecyclerView.State;
 import android.view.View;
 import android.view.View.OnFocusChangeListener;
 
 import com.android.launcher3.keyboard.FocusIndicatorHelper.SimpleFocusIndicatorHelper;
 
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.ItemDecoration;
+import androidx.recyclerview.widget.RecyclerView.State;
+
 /**
  * {@link ItemDecoration} for drawing and animating focused view background.
  */
diff --git a/src/com/android/launcher3/logging/EventLogArray.java b/src/com/android/launcher3/logging/EventLogArray.java
new file mode 100644
index 0000000..f20f365
--- /dev/null
+++ b/src/com/android/launcher3/logging/EventLogArray.java
@@ -0,0 +1,141 @@
+/*
+ * 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.logging;
+
+
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Locale;
+
+/**
+ * A utility class to record and log events. Events are stored in a fixed size array and old logs
+ * are purged as new events come.
+ */
+public class EventLogArray {
+
+    private static final int TYPE_ONE_OFF = 0;
+    private static final int TYPE_FLOAT = 1;
+    private static final int TYPE_INTEGER = 2;
+    private static final int TYPE_BOOL_TRUE = 3;
+    private static final int TYPE_BOOL_FALSE = 4;
+
+    private final String name;
+    private final EventEntry[] logs;
+    private int nextIndex;
+
+    public EventLogArray(String name, int size) {
+        this.name = name;
+        logs = new EventEntry[size];
+        nextIndex = 0;
+    }
+
+    public void addLog(String event) {
+        addLog(TYPE_ONE_OFF, event, 0);
+    }
+
+    public void addLog(String event, int extras) {
+        addLog(TYPE_INTEGER, event, extras);
+    }
+
+    public void addLog(String event, float extras) {
+        addLog(TYPE_FLOAT, event, extras);
+    }
+
+    public void addLog(String event, boolean extras) {
+        addLog(extras ? TYPE_BOOL_TRUE : TYPE_BOOL_FALSE, event, 0);
+    }
+
+    private void addLog(int type, String event, float extras) {
+        // Merge the logs if its a duplicate
+        int last = (nextIndex + logs.length - 1) % logs.length;
+        int secondLast = (nextIndex + logs.length - 2) % logs.length;
+        if (isEntrySame(logs[last], type, event) && isEntrySame(logs[secondLast], type, event)) {
+            logs[last].update(type, event, extras);
+            logs[secondLast].duplicateCount++;
+            return;
+        }
+
+        if (logs[nextIndex] == null) {
+            logs[nextIndex] = new EventEntry();
+        }
+        logs[nextIndex].update(type, event, extras);
+        nextIndex = (nextIndex + 1) % logs.length;
+    }
+
+    public void clear() {
+        Arrays.setAll(logs, (i) -> null);
+    }
+
+    public void dump(String prefix, PrintWriter writer) {
+        writer.println(prefix + "EventLog (" + name + ") history:");
+        SimpleDateFormat sdf = new SimpleDateFormat("  HH:mm:ss.SSSZ  ", Locale.US);
+        Date date = new Date();
+
+        for (int i = 0; i < logs.length; i++) {
+            EventEntry log = logs[(nextIndex + logs.length - i - 1) % logs.length];
+            if (log == null) {
+                continue;
+            }
+            date.setTime(log.time);
+
+            StringBuilder msg = new StringBuilder(prefix).append(sdf.format(date))
+                    .append(log.event);
+            switch (log.type) {
+                case TYPE_BOOL_FALSE:
+                    msg.append(": false");
+                    break;
+                case TYPE_BOOL_TRUE:
+                    msg.append(": true");
+                    break;
+                case TYPE_FLOAT:
+                    msg.append(": ").append(log.extras);
+                    break;
+                case TYPE_INTEGER:
+                    msg.append(": ").append((int) log.extras);
+                    break;
+                default: // fall out
+            }
+            if (log.duplicateCount > 0) {
+                msg.append(" & ").append(log.duplicateCount).append(" similar events");
+            }
+            writer.println(msg);
+        }
+    }
+
+    private boolean isEntrySame(EventEntry entry, int type, String event) {
+        return entry != null && entry.type == type && entry.event.equals(event);
+    }
+
+    /** A single event entry. */
+    private static class EventEntry {
+
+        private int type;
+        private String event;
+        private float extras;
+        private long time;
+        private int duplicateCount;
+
+        public void update(int type, String event, float extras) {
+            this.type = type;
+            this.event = event;
+            this.extras = extras;
+            time = System.currentTimeMillis();
+            duplicateCount = 0;
+        }
+    }
+}
diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java
index 83593aa..9b75b43 100644
--- a/src/com/android/launcher3/logging/LoggerUtils.java
+++ b/src/com/android/launcher3/logging/LoggerUtils.java
@@ -15,7 +15,8 @@
  */
 package com.android.launcher3.logging;
 
-import android.content.Context;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType.NAVBAR;
+
 import android.util.ArrayMap;
 import android.util.SparseArray;
 import android.view.View;
@@ -75,10 +76,17 @@
                 if (action.touch == Action.Touch.SWIPE || action.touch == Action.Touch.FLING) {
                     str += " direction=" + getFieldName(action.dir, Action.Direction.class);
                 }
-                return str;
-            case Action.Type.COMMAND: return getFieldName(action.command, Action.Command.class);
+                break;
+            case Action.Type.COMMAND:
+                str += getFieldName(action.command, Action.Command.class);
+                break;
             default: return getFieldName(action.type, Action.Type.class);
         }
+        if (action.touch == Action.Touch.SWIPE || action.touch == Action.Touch.FLING ||
+                (action.command == Action.Command.BACK && action.dir != Action.Direction.NONE)) {
+            str += " direction=" + getFieldName(action.dir, Action.Direction.class);
+        }
+        return str;
     }
 
     public static String getTargetStr(Target t) {
@@ -96,16 +104,21 @@
             case Target.Type.CONTAINER:
                 str = getFieldName(t.containerType, ContainerType.class);
                 if (t.containerType == ContainerType.WORKSPACE ||
-                        t.containerType == ContainerType.HOTSEAT) {
+                        t.containerType == ContainerType.HOTSEAT ||
+                        t.containerType == NAVBAR) {
                     str += " id=" + t.pageIndex;
                 } else if (t.containerType == ContainerType.FOLDER) {
-                    str += " grid(" + t.gridX + "," + t.gridY+ ")";
+                    str += " grid(" + t.gridX + "," + t.gridY + ")";
                 }
                 break;
             default:
                 str += "UNKNOWN TARGET TYPE";
         }
 
+        if (t.spanX != 1 || t.spanY != 1) {
+            str += " span(" + t.spanX + "," + t.spanY + ")";
+        }
+
         if (t.tipType != TipType.DEFAULT_NONE) {
             str += " " + getFieldName(t.tipType, TipType.class);
         }
@@ -131,6 +144,10 @@
                     + "), pageIdx=" + t.pageIndex;
 
         }
+        if (t.searchQueryLength != 0) {
+            typeStr += ", searchQueryLength=" + t.searchQueryLength;
+        }
+
         if (t.itemType == ItemType.TASK) {
             typeStr += ", pageIdx=" + t.pageIndex;
         }
@@ -144,7 +161,7 @@
     }
 
     public static Target newItemTarget(View v, InstantAppResolver instantAppResolver) {
-        return (v.getTag() instanceof ItemInfo)
+        return (v != null) && (v.getTag() instanceof ItemInfo)
                 ? newItemTarget((ItemInfo) v.getTag(), instantAppResolver)
                 : newTarget(Target.Type.ITEM);
     }
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
new file mode 100644
index 0000000..9b9543e
--- /dev/null
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -0,0 +1,44 @@
+/*
+ * 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.logging;
+
+import android.content.Context;
+import android.content.Intent;
+import android.view.View;
+
+import com.android.launcher3.R;
+import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.ResourceBasedOverride;
+import com.android.launcher3.logging.StatsLogUtils.LogStateProvider;
+
+/**
+ * Handles the user event logging in Q.
+ */
+public class StatsLogManager implements ResourceBasedOverride {
+
+    protected LogStateProvider mStateProvider;
+    public static StatsLogManager newInstance(Context context, LogStateProvider stateProvider) {
+        StatsLogManager mgr = Overrides.getObject(StatsLogManager.class,
+                context.getApplicationContext(), R.string.stats_log_manager_class);
+        mgr.mStateProvider = stateProvider;
+        mgr.verify();
+        return mgr;
+    }
+
+    public void logAppLaunch(View v, Intent intent) { }
+    public void logTaskLaunch(View v, ComponentKey key) { }
+    public void verify() {}     // TODO: should move into robo tests
+}
diff --git a/src/com/android/launcher3/logging/StatsLogUtils.java b/src/com/android/launcher3/logging/StatsLogUtils.java
new file mode 100644
index 0000000..647f255
--- /dev/null
+++ b/src/com/android/launcher3/logging/StatsLogUtils.java
@@ -0,0 +1,67 @@
+package com.android.launcher3.logging;
+
+import android.view.View;
+import android.view.ViewParent;
+
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+
+import androidx.annotation.Nullable;
+
+
+public class StatsLogUtils {
+
+    // Defined in android.stats.launcher.nano
+    // As they cannot be linked in this file, defining again.
+    public final static int LAUNCHER_STATE_BACKGROUND = 0;
+    public final static int LAUNCHER_STATE_HOME = 1;
+    public final static int LAUNCHER_STATE_OVERVIEW = 2;
+    public final static int LAUNCHER_STATE_ALLAPPS = 3;
+
+    private final static int MAXIMUM_VIEW_HIERARCHY_LEVEL = 5;
+
+    public interface LogStateProvider {
+        int getCurrentState();
+    }
+
+    /**
+     * Implemented by containers to provide a container source for a given child.
+     *
+     * Currently,
+     */
+    public interface LogContainerProvider {
+
+        /**
+         * Copies data from the source to the destination proto.
+         *
+         * @param v            source of the data
+         * @param info         source of the data
+         * @param target       dest of the data
+         * @param targetParent dest of the data
+         */
+        void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent);
+    }
+
+    /**
+     * 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
index d1e1051..bd785a1 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -32,25 +32,25 @@
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.os.SystemClock;
-import android.support.annotation.Nullable;
 import android.util.Log;
 import android.view.View;
-import android.view.ViewParent;
 
-import com.android.launcher3.DeviceProfile;
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.DropTarget;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 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 java.util.Locale;
 import java.util.UUID;
@@ -61,16 +61,14 @@
  *
  * $ adb shell setprop log.tag.UserEvent VERBOSE
  */
-public class UserEventDispatcher {
-
-    private final static int MAXIMUM_VIEW_HIERARCHY_LEVEL = 5;
+public class UserEventDispatcher implements ResourceBasedOverride {
 
     private static final String TAG = "UserEvent";
     private static final boolean IS_VERBOSE =
             FeatureFlags.IS_DOGFOOD_BUILD && Utilities.isPropertyEnabled(LogConfig.USEREVENT);
     private static final String UUID_STORAGE = "uuid";
 
-    public static UserEventDispatcher newInstance(Context context, DeviceProfile dp,
+    public static UserEventDispatcher newInstance(Context context,
             UserEventDelegate delegate) {
         SharedPreferences sharedPrefs = Utilities.getDevicePrefs(context);
         String uuidStr = sharedPrefs.getString(UUID_STORAGE, null);
@@ -78,18 +76,16 @@
             uuidStr = UUID.randomUUID().toString();
             sharedPrefs.edit().putString(UUID_STORAGE, uuidStr).apply();
         }
-        UserEventDispatcher ued = Utilities.getOverrideObject(UserEventDispatcher.class,
+        UserEventDispatcher ued = Overrides.getObject(UserEventDispatcher.class,
                 context.getApplicationContext(), R.string.user_event_dispatcher_class);
         ued.mDelegate = delegate;
-        ued.mIsInLandscapeMode = dp.isVerticalBarLayout();
-        ued.mIsInMultiWindowMode = dp.isMultiWindowMode;
         ued.mUuidStr = uuidStr;
         ued.mInstantAppResolver = InstantAppResolver.newInstance(context);
         return ued;
     }
 
-    public static UserEventDispatcher newInstance(Context context, DeviceProfile dp) {
-        return newInstance(context, dp, null);
+    public static UserEventDispatcher newInstance(Context context) {
+        return newInstance(context, null);
     }
 
     public interface UserEventDelegate {
@@ -97,50 +93,24 @@
     }
 
     /**
-     * Implemented by containers to provide a container source for a given child.
+     * Fills in the container data on the given event if the given view is not null.
+     * @return whether container data was added.
      */
-    public interface LogContainerProvider {
-
-        /**
-         * Copies data from the source to the destination proto.
-         *
-         * @param v            source of the data
-         * @param info         source of the data
-         * @param target       dest of the data
-         * @param targetParent dest of the data
-         */
-        void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent);
-    }
-
-    /**
-     * 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;
+    public static boolean fillInLogContainerData(LauncherLogProto.LauncherEvent event, @Nullable View v) {
+        // Fill in grid(x,y), pageIndex of the child and container type of the parent
+        LogContainerProvider provider = StatsLogUtils.getLaunchProviderRecursive(v);
+        if (v == null || !(v.getTag() instanceof ItemInfo) || provider == null) {
+            return false;
         }
-
-        // 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;
+        ItemInfo itemInfo = (ItemInfo) v.getTag();
+        provider.fillInLogContainerData(v, itemInfo, event.srcTarget[0], event.srcTarget[1]);
+        return true;
     }
 
     private boolean mSessionStarted;
     private long mElapsedContainerMillis;
     private long mElapsedSessionMillis;
     private long mActionDurationMillis;
-    private boolean mIsInMultiWindowMode;
-    private boolean mIsInLandscapeMode;
     private String mUuidStr;
     protected InstantAppResolver mInstantAppResolver;
     private boolean mAppOrTaskLaunch;
@@ -153,21 +123,7 @@
     // intentHash                       required
     // --------------------------------------------------------------
 
-    /**
-     * Fills in the container data on the given event if the given view is not null.
-     * @return whether container data was added.
-     */
-    protected boolean fillInLogContainerData(LauncherEvent event, @Nullable View v) {
-        // Fill in grid(x,y), pageIndex of the child and container type of the parent
-        LogContainerProvider provider = getLaunchProviderRecursive(v);
-        if (v == null || !(v.getTag() instanceof ItemInfo) || provider == null) {
-            return false;
-        }
-        ItemInfo itemInfo = (ItemInfo) v.getTag();
-        provider.fillInLogContainerData(v, itemInfo, event.srcTarget[0], event.srcTarget[1]);
-        return true;
-    }
-
+    @Deprecated
     public void logAppLaunch(View v, Intent intent) {
         LauncherEvent event = newLauncherEvent(newTouchAction(Action.Touch.TAP),
                 newItemTarget(v, mInstantAppResolver), newTarget(Target.Type.CONTAINER));
@@ -184,6 +140,7 @@
 
     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
@@ -335,7 +292,7 @@
      * (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 srcChildTargetType,
+    public void logStateChangeAction(int action, int dir, int downX, int downY, int srcChildTargetType,
                                      int srcParentContainerType, int dstContainerType,
                                      int pageIndex) {
         LauncherEvent event;
@@ -353,6 +310,8 @@
         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");
     }
@@ -366,8 +325,8 @@
     }
 
     public void logDeepShortcutsOpen(View icon) {
-        LogContainerProvider provider = getLaunchProviderRecursive(icon);
-        if (icon == null || !(icon.getTag() instanceof ItemInfo)) {
+        LogContainerProvider provider = StatsLogUtils.getLaunchProviderRecursive(icon);
+        if (icon == null || !(icon.getTag() instanceof ItemInfo || provider == null)) {
             return;
         }
         ItemInfo info = (ItemInfo) icon.getTag();
@@ -379,15 +338,6 @@
         resetElapsedContainerMillis("deep shortcut open");
     }
 
-    /* Currently we are only interested in whether this event happens or not and don't
-    * care about which screen moves to where. */
-    public void logOverviewReorder() {
-        LauncherEvent event = newLauncherEvent(newTouchAction(Action.Touch.DRAGDROP),
-                newContainerTarget(ContainerType.WORKSPACE),
-                newContainerTarget(ContainerType.OVERVIEW));
-        dispatchUserEvent(event, null);
-    }
-
     public void logDragNDrop(DropTarget.DragObject dragObj, View dropTargetAsView) {
         LauncherEvent event = newLauncherEvent(newTouchAction(Action.Touch.DRAGDROP),
                 newItemTarget(dragObj.originalDragInfo, mInstantAppResolver),
@@ -409,6 +359,27 @@
         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
+                ? LauncherLogProto.ControlType.BACK_BUTTON
+                : LauncherLogProto.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.
      * @param reason
@@ -434,8 +405,6 @@
 
     public void dispatchUserEvent(LauncherEvent ev, Intent intent) {
         mAppOrTaskLaunch = false;
-        ev.isInLandscapeMode = mIsInLandscapeMode;
-        ev.isInMultiWindowMode = mIsInMultiWindowMode;
         ev.elapsedContainerMillis = SystemClock.uptimeMillis() - mElapsedContainerMillis;
         ev.elapsedSessionMillis = SystemClock.uptimeMillis() - mElapsedSessionMillis;
 
@@ -455,8 +424,6 @@
                 ev.elapsedContainerMillis,
                 ev.elapsedSessionMillis,
                 ev.actionDurationMillis);
-        log += "\n isInLandscapeMode " + ev.isInLandscapeMode;
-        log += "\n isInMultiWindowMode " + ev.isInMultiWindowMode;
         log += "\n\n";
         Log.d(TAG, log);
     }
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index fefe07d..ed0d470 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -15,11 +15,11 @@
  */
 package com.android.launcher3.model;
 
-import android.content.Context;
 import android.content.Intent;
 import android.os.UserHandle;
 import android.util.LongSparseArray;
 import android.util.Pair;
+
 import com.android.launcher3.AllAppsList;
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.FolderInfo;
@@ -27,13 +27,14 @@
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherAppWidgetInfo;
-import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherModel.CallbackTask;
 import com.android.launcher3.LauncherModel.Callbacks;
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.util.GridOccupancy;
+import com.android.launcher3.util.IntArray;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -56,16 +57,12 @@
         if (mItemList.isEmpty()) {
             return;
         }
-        Context context = app.getContext();
 
         final ArrayList<ItemInfo> addedItemsFinal = new ArrayList<>();
-        final ArrayList<Long> addedWorkspaceScreensFinal = new ArrayList<>();
+        final IntArray addedWorkspaceScreensFinal = new IntArray();
 
-        // Get the list of workspace screens.  We need to append to this list and
-        // can not use sBgWorkspaceScreens because loadWorkspace() may not have been
-        // called.
-        ArrayList<Long> workspaceScreens = LauncherModel.loadWorkspaceScreensDb(context);
         synchronized(dataModel) {
+            IntArray workspaceScreens = dataModel.collectWorkspaceScreens();
 
             List<ItemInfo> filteredItems = new ArrayList<>();
             for (Pair<ItemInfo, Object> entry : mItemList) {
@@ -80,7 +77,7 @@
 
                 if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
                     if (item instanceof AppInfo) {
-                        item = ((AppInfo) item).makeShortcut();
+                        item = ((AppInfo) item).makeWorkspaceItem();
                     }
                 }
                 if (item != null) {
@@ -90,17 +87,16 @@
 
             for (ItemInfo item : filteredItems) {
                 // Find appropriate space for the item.
-                Pair<Long, int[]> coords = findSpaceForItem(app, dataModel, workspaceScreens,
+                int[] coords = findSpaceForItem(app, dataModel, workspaceScreens,
                         addedWorkspaceScreensFinal, item.spanX, item.spanY);
-                long screenId = coords.first;
-                int[] cordinates = coords.second;
+                int screenId = coords[0];
 
                 ItemInfo itemInfo;
-                if (item instanceof ShortcutInfo || item instanceof FolderInfo ||
+                if (item instanceof WorkspaceItemInfo || item instanceof FolderInfo ||
                         item instanceof LauncherAppWidgetInfo) {
                     itemInfo = item;
                 } else if (item instanceof AppInfo) {
-                    itemInfo = ((AppInfo) item).makeShortcut();
+                    itemInfo = ((AppInfo) item).makeWorkspaceItem();
                 } else {
                     throw new RuntimeException("Unexpected info type");
                 }
@@ -108,16 +104,13 @@
                 // Add the shortcut to the db
                 getModelWriter().addItemToDatabase(itemInfo,
                         LauncherSettings.Favorites.CONTAINER_DESKTOP, screenId,
-                        cordinates[0], cordinates[1]);
+                        coords[1], coords[2]);
 
-                // Save the ShortcutInfo for binding in the workspace
+                // Save the WorkspaceItemInfo for binding in the workspace
                 addedItemsFinal.add(itemInfo);
             }
         }
 
-        // Update the workspace screens
-        updateScreens(context, workspaceScreens);
-
         if (!addedItemsFinal.isEmpty()) {
             scheduleCallbackTask(new CallbackTask() {
                 @Override
@@ -126,7 +119,7 @@
                     final ArrayList<ItemInfo> addNotAnimated = new ArrayList<>();
                     if (!addedItemsFinal.isEmpty()) {
                         ItemInfo info = addedItemsFinal.get(addedItemsFinal.size() - 1);
-                        long lastScreenId = info.screenId;
+                        int lastScreenId = info.screenId;
                         for (ItemInfo i : addedItemsFinal) {
                             if (i.screenId == lastScreenId) {
                                 addAnimated.add(i);
@@ -142,10 +135,6 @@
         }
     }
 
-    protected void updateScreens(Context context, ArrayList<Long> workspaceScreens) {
-        LauncherModel.updateWorkspaceScreenOrder(context, workspaceScreens);
-    }
-
     /**
      * Returns true if the shortcuts already exists on the workspace. This must be called after
      * the workspace has been loaded. We identify a shortcut by its intent.
@@ -176,8 +165,8 @@
         boolean isLauncherAppTarget = Utilities.isLauncherAppTarget(intent);
         synchronized (dataModel) {
             for (ItemInfo item : dataModel.itemsIdMap) {
-                if (item instanceof ShortcutInfo) {
-                    ShortcutInfo info = (ShortcutInfo) item;
+                if (item instanceof WorkspaceItemInfo) {
+                    WorkspaceItemInfo info = (WorkspaceItemInfo) item;
                     if (item.getIntent() != null && info.user.equals(user)) {
                         Intent copyIntent = new Intent(item.getIntent());
                         copyIntent.setSourceBounds(intent.getSourceBounds());
@@ -189,7 +178,7 @@
                         // checking for existing promise icon with same package name
                         if (isLauncherAppTarget
                                 && info.isPromise()
-                                && info.hasStatusFlag(ShortcutInfo.FLAG_AUTOINSTALL_ICON)
+                                && info.hasStatusFlag(WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON)
                                 && info.getTargetComponent() != null
                                 && compPkgName != null
                                 && compPkgName.equals(info.getTargetComponent().getPackageName())) {
@@ -204,13 +193,10 @@
 
     /**
      * Find a position on the screen for the given size or adds a new screen.
-     * @return screenId and the coordinates for the item.
+     * @return screenId and the coordinates for the item in an int array of size 3.
      */
-    protected Pair<Long, int[]> findSpaceForItem(
-            LauncherAppState app, BgDataModel dataModel,
-            ArrayList<Long> workspaceScreens,
-            ArrayList<Long> addedWorkspaceScreensFinal,
-            int spanX, int spanY) {
+    protected int[] findSpaceForItem( LauncherAppState app, BgDataModel dataModel,
+            IntArray workspaceScreens, IntArray addedWorkspaceScreensFinal, int spanX, int spanY) {
         LongSparseArray<ArrayList<ItemInfo>> screenItems = new LongSparseArray<>();
 
         // Use sBgItemsIdMap as all the items are already loaded.
@@ -228,7 +214,7 @@
         }
 
         // Find appropriate space for the item.
-        long screenId = 0;
+        int screenId = 0;
         int[] cordinates = new int[2];
         boolean found = false;
 
@@ -258,7 +244,7 @@
             // Still no position found. Add a new screen to the end.
             screenId = LauncherSettings.Settings.call(app.getContext().getContentResolver(),
                     LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
-                    .getLong(LauncherSettings.Settings.EXTRA_VALUE);
+                    .getInt(LauncherSettings.Settings.EXTRA_VALUE);
 
             // Save the screen id for binding in the workspace
             workspaceScreens.add(screenId);
@@ -270,7 +256,7 @@
                 throw new RuntimeException("Can't find space to add the item");
             }
         }
-        return Pair.create(screenId, cordinates);
+        return new int[] {screenId, cordinates[0], cordinates[1]};
     }
 
     private boolean findNextAvailableIconSpaceInScreen(
diff --git a/src/com/android/launcher3/model/AppLaunchTracker.java b/src/com/android/launcher3/model/AppLaunchTracker.java
new file mode 100644
index 0000000..1613d47
--- /dev/null
+++ b/src/com/android/launcher3/model/AppLaunchTracker.java
@@ -0,0 +1,56 @@
+/*
+ * 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.ResourceBasedOverride.Overrides.getObject;
+
+import android.content.ComponentName;
+import android.os.UserHandle;
+
+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;
+
+import androidx.annotation.Nullable;
+
+/**
+ * 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 MainThreadInitializedObject<AppLaunchTracker> INSTANCE =
+            new MainThreadInitializedObject<>(c ->
+                    getObject(AppLaunchTracker.class, c, 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 onReturnedToHome() { }
+}
diff --git a/src/com/android/launcher3/model/BaseLoaderResults.java b/src/com/android/launcher3/model/BaseLoaderResults.java
new file mode 100644
index 0000000..97cf267
--- /dev/null
+++ b/src/com/android/launcher3/model/BaseLoaderResults.java
@@ -0,0 +1,310 @@
+/*
+ * 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;
+
+import android.os.Looper;
+import android.util.Log;
+
+import com.android.launcher3.AllAppsList;
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.LauncherModel.CallbackTask;
+import com.android.launcher3.LauncherModel.Callbacks;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.PagedView;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.IntSet;
+import com.android.launcher3.util.LooperIdleLock;
+import com.android.launcher3.util.ViewOnDrawExecutor;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.concurrent.Executor;
+
+/**
+ * Base Helper class to handle results of {@link com.android.launcher3.model.LoaderTask}.
+ */
+public abstract class BaseLoaderResults {
+
+    protected static final String TAG = "LoaderResults";
+    protected static final int INVALID_SCREEN_ID = -1;
+    private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons
+
+    protected final Executor mUiExecutor;
+
+    protected final LauncherAppState mApp;
+    protected final BgDataModel mBgDataModel;
+    private final AllAppsList mBgAllAppsList;
+    protected final int mPageToBindFirst;
+
+    protected final WeakReference<Callbacks> mCallbacks;
+
+    private int mMyBindingId;
+
+    public BaseLoaderResults(LauncherAppState app, BgDataModel dataModel,
+            AllAppsList allAppsList, int pageToBindFirst, WeakReference<Callbacks> callbacks) {
+        mUiExecutor = new MainThreadExecutor();
+        mApp = app;
+        mBgDataModel = dataModel;
+        mBgAllAppsList = allAppsList;
+        mPageToBindFirst = pageToBindFirst;
+        mCallbacks = callbacks == null ? new WeakReference<>(null) : callbacks;
+    }
+
+    /**
+     * Binds all loaded data to actual views on the main thread.
+     */
+    public void bindWorkspace() {
+        Callbacks callbacks = mCallbacks.get();
+        // Don't use these two variables in any of the callback runnables.
+        // Otherwise we hold a reference to them.
+        if (callbacks == null) {
+            // This launcher has exited and nobody bothered to tell us.  Just bail.
+            Log.w(TAG, "LoaderTask running with no launcher");
+            return;
+        }
+
+        // Save a copy of all the bg-thread collections
+        ArrayList<ItemInfo> workspaceItems = new ArrayList<>();
+        ArrayList<LauncherAppWidgetInfo> appWidgets = new ArrayList<>();
+        final IntArray orderedScreenIds = new IntArray();
+
+        synchronized (mBgDataModel) {
+            workspaceItems.addAll(mBgDataModel.workspaceItems);
+            appWidgets.addAll(mBgDataModel.appWidgets);
+            orderedScreenIds.addAll(mBgDataModel.collectWorkspaceScreens());
+            mBgDataModel.lastBindId++;
+            mMyBindingId = mBgDataModel.lastBindId;
+        }
+
+        final int currentScreen;
+        {
+            int currScreen = mPageToBindFirst != PagedView.INVALID_RESTORE_PAGE
+                    ? mPageToBindFirst : callbacks.getCurrentWorkspaceScreen();
+            if (currScreen >= orderedScreenIds.size()) {
+                // There may be no workspace screens (just hotseat items and an empty page).
+                currScreen = PagedView.INVALID_RESTORE_PAGE;
+            }
+            currentScreen = currScreen;
+        }
+        final boolean validFirstPage = currentScreen >= 0;
+        final int currentScreenId =
+                validFirstPage ? orderedScreenIds.get(currentScreen) : INVALID_SCREEN_ID;
+
+        // 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(currentScreenId, workspaceItems, currentWorkspaceItems,
+                otherWorkspaceItems);
+        filterCurrentWorkspaceItems(currentScreenId, appWidgets, currentAppWidgets,
+                otherAppWidgets);
+        sortWorkspaceItemsSpatially(currentWorkspaceItems);
+        sortWorkspaceItemsSpatially(otherWorkspaceItems);
+
+        // Tell the workspace that we're about to start binding items
+        executeCallbacksTask(c -> {
+            c.clearPendingBinds();
+            c.startBinding();
+        }, mUiExecutor);
+
+        // Bind workspace screens
+        executeCallbacksTask(c -> c.bindScreens(orderedScreenIds), mUiExecutor);
+
+        Executor mainExecutor = mUiExecutor;
+        // Load items on the current page.
+        bindWorkspaceItems(currentWorkspaceItems, mainExecutor);
+        bindAppWidgets(currentAppWidgets, 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;
+
+        executeCallbacksTask(c -> c.finishFirstPageBind(
+                validFirstPage ? (ViewOnDrawExecutor) deferredExecutor : null), mainExecutor);
+
+        bindWorkspaceItems(otherWorkspaceItems, deferredExecutor);
+        bindAppWidgets(otherAppWidgets, deferredExecutor);
+        // Tell the workspace that we're done binding items
+        executeCallbacksTask(c -> c.finishBindingItems(mPageToBindFirst), deferredExecutor);
+
+        if (validFirstPage) {
+            executeCallbacksTask(c -> {
+                // We are loading synchronously, which means, some of the pages will be
+                // bound after first draw. Inform the callbacks that page binding is
+                // not complete, and schedule the remaining pages.
+                if (currentScreen != PagedView.INVALID_RESTORE_PAGE) {
+                    c.onPageBoundSynchronously(currentScreen);
+                }
+                c.executeOnNextDraw((ViewOnDrawExecutor) deferredExecutor);
+
+            }, mUiExecutor);
+        }
+    }
+
+
+    /** Filters the set of items who are directly or indirectly (via another container) on the
+     * specified screen. */
+    public static <T extends ItemInfo> void filterCurrentWorkspaceItems(int currentScreenId,
+            ArrayList<T> allWorkspaceItems,
+            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();
+            }
+        }
+
+        // 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.
+        IntSet itemsOnScreen = new IntSet();
+        Collections.sort(allWorkspaceItems,
+                (lhs, rhs) -> Integer.compare(lhs.container, rhs.container));
+
+        for (T info : allWorkspaceItems) {
+            if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+                if (info.screenId == currentScreenId) {
+                    currentScreenItems.add(info);
+                    itemsOnScreen.add(info.id);
+                } else {
+                    otherScreenItems.add(info);
+                }
+            } else if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
+                currentScreenItems.add(info);
+                itemsOnScreen.add(info.id);
+            } else {
+                if (itemsOnScreen.contains(info.container)) {
+                    currentScreenItems.add(info);
+                    itemsOnScreen.add(info.id);
+                } else {
+                    otherScreenItems.add(info);
+                }
+            }
+        }
+    }
+
+    /** Sorts the set of items by hotseat, workspace (spatially from top to bottom, left to
+     * right) */
+    protected void sortWorkspaceItemsSpatially(ArrayList<ItemInfo> workspaceItems) {
+        final InvariantDeviceProfile profile = mApp.getInvariantDeviceProfile();
+        final int screenCols = profile.numColumns;
+        final int screenCellCount = profile.numColumns * profile.numRows;
+        Collections.sort(workspaceItems, new Comparator<ItemInfo>() {
+            @Override
+            public int compare(ItemInfo lhs, ItemInfo rhs) {
+                if (lhs.container == rhs.container) {
+                    // Within containers, order by their spatial position in that container
+                    switch (lhs.container) {
+                        case LauncherSettings.Favorites.CONTAINER_DESKTOP: {
+                            int lr = (lhs.screenId * screenCellCount +
+                                    lhs.cellY * screenCols + lhs.cellX);
+                            int rr = (rhs.screenId * screenCellCount +
+                                    rhs.cellY * screenCols + rhs.cellX);
+                            return Integer.compare(lr, rr);
+                        }
+                        case LauncherSettings.Favorites.CONTAINER_HOTSEAT: {
+                            // We currently use the screen id as the rank
+                            return Integer.compare(lhs.screenId, rhs.screenId);
+                        }
+                        default:
+                            if (FeatureFlags.IS_DOGFOOD_BUILD) {
+                                throw new RuntimeException("Unexpected container type when " +
+                                        "sorting workspace items.");
+                            }
+                            return 0;
+                    }
+                } else {
+                    // Between containers, order by hotseat, desktop
+                    return Integer.compare(lhs.container, rhs.container);
+                }
+            }
+        });
+    }
+
+    protected void bindWorkspaceItems(final ArrayList<ItemInfo> workspaceItems,
+            final Executor executor) {
+        // Bind the workspace items
+        int N = workspaceItems.size();
+        for (int i = 0; i < N; i += ITEMS_CHUNK) {
+            final int start = i;
+            final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
+            executeCallbacksTask(
+                    c -> c.bindItems(workspaceItems.subList(start, start + chunkSize), false),
+                    executor);
+        }
+    }
+
+    private void bindAppWidgets(ArrayList<LauncherAppWidgetInfo> appWidgets, Executor executor) {
+        int N;// Bind the widgets, one at a time
+        N = appWidgets.size();
+        for (int i = 0; i < N; i++) {
+            final ItemInfo widget = appWidgets.get(i);
+            executeCallbacksTask(
+                    c -> c.bindItems(Collections.singletonList(widget), false), executor);
+        }
+    }
+
+    public abstract void bindDeepShortcuts();
+
+    public void bindAllApps() {
+        // shallow copy
+        @SuppressWarnings("unchecked")
+        ArrayList<AppInfo> list = (ArrayList<AppInfo>) mBgAllAppsList.data.clone();
+        executeCallbacksTask(c -> c.bindAllApplications(list), mUiExecutor);
+    }
+
+    public abstract void bindWidgets();
+
+    protected void executeCallbacksTask(CallbackTask task, Executor executor) {
+        executor.execute(() -> {
+            if (mMyBindingId != mBgDataModel.lastBindId) {
+                Log.d(TAG, "Too many consecutive reloads, skipping obsolete data-bind");
+                return;
+            }
+            Callbacks callbacks = mCallbacks.get();
+            if (callbacks != null) {
+                task.execute(callbacks);
+            }
+        });
+    }
+
+    public LooperIdleLock newIdleLock(Object lock) {
+        LooperIdleLock idleLock = new LooperIdleLock(lock, Looper.getMainLooper());
+        // If we are not binding, there is no reason to wait for idle.
+        if (mCallbacks.get() == null) {
+            idleLock.queueIdle();
+        }
+        return idleLock;
+    }
+}
diff --git a/src/com/android/launcher3/model/BaseModelUpdateTask.java b/src/com/android/launcher3/model/BaseModelUpdateTask.java
index fcdc088..eea3d8c 100644
--- a/src/com/android/launcher3/model/BaseModelUpdateTask.java
+++ b/src/com/android/launcher3/model/BaseModelUpdateTask.java
@@ -15,7 +15,6 @@
  */
 package com.android.launcher3.model;
 
-import android.os.UserHandle;
 import android.util.Log;
 
 import com.android.launcher3.AllAppsList;
@@ -24,13 +23,13 @@
 import com.android.launcher3.LauncherModel.ModelUpdateTask;
 import com.android.launcher3.LauncherModel.CallbackTask;
 import com.android.launcher3.LauncherModel.Callbacks;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.widget.WidgetListRowEntry;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.concurrent.Executor;
 
 /**
@@ -94,26 +93,21 @@
     }
 
 
-    public void bindUpdatedShortcuts(
-            final ArrayList<ShortcutInfo> updatedShortcuts, final UserHandle user) {
+    public void bindUpdatedWorkspaceItems(final ArrayList<WorkspaceItemInfo> updatedShortcuts) {
         if (!updatedShortcuts.isEmpty()) {
             scheduleCallbackTask(new CallbackTask() {
                 @Override
                 public void execute(Callbacks callbacks) {
-                    callbacks.bindShortcutsChanged(updatedShortcuts, user);
+                    callbacks.bindWorkspaceItemsChanged(updatedShortcuts);
                 }
             });
         }
     }
 
     public void bindDeepShortcuts(BgDataModel dataModel) {
-        final MultiHashMap<ComponentKey, String> shortcutMapCopy = dataModel.deepShortcutMap.clone();
-        scheduleCallbackTask(new CallbackTask() {
-            @Override
-            public void execute(Callbacks callbacks) {
-                callbacks.bindDeepShortcutMap(shortcutMapCopy);
-            }
-        });
+        final HashMap<ComponentKey, Integer> shortcutMapCopy =
+                new HashMap<>(dataModel.deepShortcutMap);
+        scheduleCallbackTask(callbacks -> callbacks.bindDeepShortcutMap(shortcutMapCopy));
     }
 
     public void bindUpdatedWidgets(BgDataModel dataModel) {
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index fff1e69..8f0cd08 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -16,6 +16,7 @@
 package com.android.launcher3.model;
 
 import android.content.Context;
+import android.content.pm.ShortcutInfo;
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
@@ -26,18 +27,19 @@
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.Workspace;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.DumpTargetWrapper;
 import com.android.launcher3.model.nano.LauncherDumpProto;
 import com.android.launcher3.model.nano.LauncherDumpProto.ContainerType;
 import com.android.launcher3.model.nano.LauncherDumpProto.DumpTarget;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.LongArrayMap;
-import com.android.launcher3.util.MultiHashMap;
+import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.IntSet;
+import com.android.launcher3.util.IntSparseArrayMap;
 import com.google.protobuf.nano.MessageNano;
 
 import java.io.FileDescriptor;
@@ -62,7 +64,7 @@
      * Map of all the ItemInfos (shortcuts, folders, and widgets) created by
      * LauncherModel to their ids
      */
-    public final LongArrayMap<ItemInfo> itemsIdMap = new LongArrayMap<>();
+    public final IntSparseArrayMap<ItemInfo> itemsIdMap = new IntSparseArrayMap<>();
 
     /**
      * List of all the folders and shortcuts directly on the home screen (no widgets
@@ -78,12 +80,7 @@
     /**
      * Map of id to FolderInfos of all the folders created by LauncherModel
      */
-    public final LongArrayMap<FolderInfo> folders = new LongArrayMap<>();
-
-    /**
-     * Ordered list of workspace screens ids.
-     */
-    public final ArrayList<Long> workspaceScreens = new ArrayList<>();
+    public final IntSparseArrayMap<FolderInfo> folders = new IntSparseArrayMap<>();
 
     /**
      * Map of ShortcutKey to the number of times it is pinned.
@@ -96,9 +93,9 @@
     public boolean hasShortcutHostPermission;
 
     /**
-     * Maps all launcher activities to the id's of their shortcuts (if they have any).
+     * Maps all launcher activities to counts of their shortcuts.
      */
-    public final MultiHashMap<ComponentKey, String> deepShortcutMap = new MultiHashMap<>();
+    public final HashMap<ComponentKey, Integer> deepShortcutMap = new HashMap<>();
 
     /**
      * Entire list of widgets.
@@ -118,11 +115,26 @@
         appWidgets.clear();
         folders.clear();
         itemsIdMap.clear();
-        workspaceScreens.clear();
         pinnedShortcutCounts.clear();
         deepShortcutMap.clear();
     }
 
+    /**
+     * Creates an array of valid workspace screens based on current items in the model.
+     */
+    public synchronized IntArray collectWorkspaceScreens() {
+        IntSet screenSet = new IntSet();
+        for (ItemInfo item: itemsIdMap) {
+            if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+                screenSet.add(item.screenId);
+            }
+        }
+        if (FeatureFlags.QSB_ON_FIRST_SCREEN || screenSet.isEmpty()) {
+            screenSet.add(Workspace.FIRST_SCREEN_ID);
+        }
+        return screenSet.getArray();
+    }
+
     public synchronized void dump(String prefix, FileDescriptor fd, PrintWriter writer,
             String[] args) {
         if (Arrays.asList(args).contains("--proto")) {
@@ -130,11 +142,6 @@
             return;
         }
         writer.println(prefix + "Data Model:");
-        writer.print(prefix + " ---- workspace screens: ");
-        for (int i = 0; i < workspaceScreens.size(); i++) {
-            writer.print(" " + workspaceScreens.get(i).toString());
-        }
-        writer.println();
         writer.println(prefix + " ---- workspace items ");
         for (int i = 0; i < workspaceItems.size(); i++) {
             writer.println(prefix + '\t' + workspaceItems.get(i).toString());
@@ -153,14 +160,11 @@
         }
 
         if (args.length > 0 && TextUtils.equals(args[0], "--all")) {
-            writer.println(prefix + "shortcuts");
-            for (ArrayList<String> map : deepShortcutMap.values()) {
-                writer.print(prefix + "  ");
-                for (String str : map) {
-                    writer.print(str + ", ");
-                }
-                writer.println();
+            writer.println(prefix + "shortcut counts ");
+            for (Integer count : deepShortcutMap.values()) {
+                writer.print(count + ", ");
             }
+            writer.println();
         }
     }
 
@@ -169,7 +173,8 @@
 
         // Add top parent nodes. (L1)
         DumpTargetWrapper hotseat = new DumpTargetWrapper(ContainerType.HOTSEAT, 0);
-        LongArrayMap<DumpTargetWrapper> workspaces = new LongArrayMap<>();
+        IntSparseArrayMap<DumpTargetWrapper> workspaces = new IntSparseArrayMap<>();
+        IntArray workspaceScreens = collectWorkspaceScreens();
         for (int i = 0; i < workspaceScreens.size(); i++) {
             workspaces.put(workspaceScreens.get(i),
                     new DumpTargetWrapper(ContainerType.WORKSPACE, i));
@@ -180,7 +185,7 @@
             FolderInfo fInfo = folders.valueAt(i);
             dtw = new DumpTargetWrapper(ContainerType.FOLDER, folders.size());
             dtw.writeToDumpTarget(fInfo);
-            for(ShortcutInfo sInfo: fInfo.contents) {
+            for(WorkspaceItemInfo sInfo: fInfo.contents) {
                 DumpTargetWrapper child = new DumpTargetWrapper(sInfo);
                 child.writeToDumpTarget(sInfo);
                 dtw.add(child);
@@ -330,7 +335,7 @@
                             Log.e(TAG, msg);
                         }
                     } else {
-                        findOrMakeFolder(item.container).add((ShortcutInfo) item, false);
+                        findOrMakeFolder(item.container).add((WorkspaceItemInfo) item, false);
                     }
 
                 }
@@ -346,7 +351,7 @@
      * Return an existing FolderInfo object if we have encountered this ID previously,
      * or make a new one.
      */
-    public synchronized FolderInfo findOrMakeFolder(long id) {
+    public synchronized FolderInfo findOrMakeFolder(int id) {
         // See if a placeholder was created for us already
         FolderInfo folderInfo = folders.get(id);
         if (folderInfo == null) {
@@ -358,10 +363,10 @@
     }
 
     /**
-     * Clear all the deep shortcuts for the given package, and re-add the new shortcuts.
+     * Clear all the deep shortcut counts for the given package, and re-add the new shortcut counts.
      */
-    public synchronized void updateDeepShortcutMap(
-            String packageName, UserHandle user, List<ShortcutInfoCompat> shortcuts) {
+    public synchronized void updateDeepShortcutCounts(
+            String packageName, UserHandle user, List<ShortcutInfo> shortcuts) {
         if (packageName != null) {
             Iterator<ComponentKey> keysIter = deepShortcutMap.keySet().iterator();
             while (keysIter.hasNext()) {
@@ -374,13 +379,15 @@
         }
 
         // Now add the new shortcuts to the map.
-        for (ShortcutInfoCompat shortcut : shortcuts) {
+        for (ShortcutInfo shortcut : shortcuts) {
             boolean shouldShowInContainer = shortcut.isEnabled()
                     && (shortcut.isDeclaredInManifest() || shortcut.isDynamic());
             if (shouldShowInContainer) {
                 ComponentKey targetComponent
                         = new ComponentKey(shortcut.getActivity(), shortcut.getUserHandle());
-                deepShortcutMap.addToList(targetComponent, shortcut.getId());
+
+                Integer previousCount = deepShortcutMap.get(targetComponent);
+                deepShortcutMap.put(targetComponent, previousCount == null ? 1 : previousCount + 1);
             }
         }
     }
diff --git a/src/com/android/launcher3/model/CacheDataUpdatedTask.java b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
index 0139bd9..7852444 100644
--- a/src/com/android/launcher3/model/CacheDataUpdatedTask.java
+++ b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
@@ -20,13 +20,13 @@
 
 import com.android.launcher3.AllAppsList;
 import com.android.launcher3.AppInfo;
-import com.android.launcher3.IconCache;
+import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherModel.CallbackTask;
 import com.android.launcher3.LauncherModel.Callbacks;
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.ShortcutInfo;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -55,23 +55,23 @@
 
         final ArrayList<AppInfo> updatedApps = new ArrayList<>();
 
-        ArrayList<ShortcutInfo> updatedShortcuts = new ArrayList<>();
+        ArrayList<WorkspaceItemInfo> updatedShortcuts = new ArrayList<>();
         synchronized (dataModel) {
             for (ItemInfo info : dataModel.itemsIdMap) {
-                if (info instanceof ShortcutInfo && mUser.equals(info.user)) {
-                    ShortcutInfo si = (ShortcutInfo) info;
+                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);
+                        iconCache.getTitleAndIcon(si, si.usingLowResIcon());
                         updatedShortcuts.add(si);
                     }
                 }
             }
             apps.updateIconsAndLabels(mPackages, mUser, updatedApps);
         }
-        bindUpdatedShortcuts(updatedShortcuts, mUser);
+        bindUpdatedWorkspaceItems(updatedShortcuts);
 
         if (!updatedApps.isEmpty()) {
             scheduleCallbackTask(new CallbackTask() {
@@ -83,7 +83,7 @@
         }
     }
 
-    public boolean isValidShortcut(ShortcutInfo si) {
+    public boolean isValidShortcut(WorkspaceItemInfo si) {
         switch (mOp) {
             case OP_CACHE_UPDATE:
                 return true;
diff --git a/src/com/android/launcher3/model/DbDowngradeHelper.java b/src/com/android/launcher3/model/DbDowngradeHelper.java
index cd86b72..e5c44d1 100644
--- a/src/com/android/launcher3/model/DbDowngradeHelper.java
+++ b/src/com/android/launcher3/model/DbDowngradeHelper.java
@@ -21,6 +21,7 @@
 import android.util.Log;
 import android.util.SparseArray;
 
+import com.android.launcher3.R;
 import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
 import com.android.launcher3.util.IOUtils;
 
@@ -87,8 +88,7 @@
         return helper;
     }
 
-    public static void updateSchemaFile(File schemaFile, int expectedVersion,
-            Context context, int schemaResId) {
+    public static void updateSchemaFile(File schemaFile, int expectedVersion, Context context) {
         try {
             if (DbDowngradeHelper.parse(schemaFile).version >= expectedVersion) {
                 return;
@@ -99,7 +99,7 @@
 
         // Write the updated schema
         try (FileOutputStream fos = new FileOutputStream(schemaFile);
-            InputStream in = context.getResources().openRawResource(schemaResId)) {
+            InputStream in = context.getResources().openRawResource(R.raw.downgrade_schema)) {
             IOUtils.copy(in, fos);
         } catch (IOException e) {
             Log.e(TAG, "Error writing schema file", e);
diff --git a/src/com/android/launcher3/model/GridBackupTable.java b/src/com/android/launcher3/model/GridBackupTable.java
new file mode 100644
index 0000000..804a040
--- /dev/null
+++ b/src/com/android/launcher3/model/GridBackupTable.java
@@ -0,0 +1,135 @@
+/*
+ * 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.model;
+
+import static com.android.launcher3.LauncherSettings.Favorites.BACKUP_TABLE_NAME;
+import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
+import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.graphics.Point;
+import android.os.Process;
+import android.util.Log;
+
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.LauncherSettings.Settings;
+import com.android.launcher3.compat.UserManagerCompat;
+
+/**
+ * Helper class to backup and restore Favorites table into a separate table
+ * within the same data base.
+ */
+public class GridBackupTable {
+    private static final String TAG = "GridBackupTable";
+
+    private static final int ID_PROPERTY = -1;
+
+    private static final String KEY_HOTSEAT_SIZE = Favorites.SCREEN;
+    private static final String KEY_GRID_X_SIZE = Favorites.SPANX;
+    private static final String KEY_GRID_Y_SIZE = Favorites.SPANY;
+    private static final String KEY_DB_VERSION = Favorites.RANK;
+
+    private final Context mContext;
+    private final SQLiteDatabase mDb;
+
+    private final int mOldHotseatSize;
+    private final int mOldGridX;
+    private final int mOldGridY;
+
+    private int mRestoredHotseatSize;
+    private int mRestoredGridX;
+    private int mRestoredGridY;
+
+    public GridBackupTable(Context context, SQLiteDatabase db,
+            int hotseatSize, int gridX, int gridY) {
+        mContext = context;
+        mDb = db;
+
+        mOldHotseatSize = hotseatSize;
+        mOldGridX = gridX;
+        mOldGridY = gridY;
+    }
+
+    public boolean backupOrRestoreAsNeeded() {
+        // Check if backup table exists
+        if (!tableExists(mDb, BACKUP_TABLE_NAME)) {
+            if (Settings.call(mContext.getContentResolver(), Settings.METHOD_WAS_EMPTY_DB_CREATED)
+                    .getBoolean(Settings.EXTRA_VALUE, false)) {
+                // No need to copy if empty DB was created.
+                return false;
+            }
+
+            copyTable(Favorites.TABLE_NAME, BACKUP_TABLE_NAME);
+            encodeDBProperties();
+            return false;
+        }
+
+        if (!loadDbProperties()) {
+            return false;
+        }
+        copyTable(BACKUP_TABLE_NAME, Favorites.TABLE_NAME);
+        Log.d(TAG, "Backup table found");
+        return true;
+    }
+
+    public int getRestoreHotseatAndGridSize(Point outGridSize) {
+        outGridSize.set(mRestoredGridX, mRestoredGridY);
+        return mRestoredHotseatSize;
+    }
+
+    private void copyTable(String from, String to) {
+        long userSerial = UserManagerCompat.getInstance(mContext).getSerialNumberForUser(
+                Process.myUserHandle());
+        dropTable(mDb, to);
+        Favorites.addTableToDb(mDb, userSerial, false, to);
+        mDb.execSQL("INSERT INTO " + to + " SELECT * FROM " + from + " where _id > " + ID_PROPERTY);
+    }
+
+    private void encodeDBProperties() {
+        ContentValues values = new ContentValues();
+        values.put(Favorites._ID, ID_PROPERTY);
+        values.put(KEY_DB_VERSION, mDb.getVersion());
+        values.put(KEY_GRID_X_SIZE, mOldGridX);
+        values.put(KEY_GRID_Y_SIZE, mOldGridY);
+        values.put(KEY_HOTSEAT_SIZE, mOldHotseatSize);
+        mDb.insert(BACKUP_TABLE_NAME, null, values);
+    }
+
+    private boolean loadDbProperties() {
+        try (Cursor c = mDb.query(BACKUP_TABLE_NAME, new String[] {
+                        KEY_DB_VERSION,     // 0
+                        KEY_GRID_X_SIZE,    // 1
+                        KEY_GRID_Y_SIZE,    // 2
+                        KEY_HOTSEAT_SIZE},  // 3
+                "_id=" + ID_PROPERTY, null, null, null, null)) {
+            if (!c.moveToNext()) {
+                Log.e(TAG, "Meta data not found in backup table");
+                return false;
+            }
+            if (mDb.getVersion() != c.getInt(0)) {
+                return false;
+            }
+
+            mRestoredGridX = c.getInt(1);
+            mRestoredGridY = c.getInt(2);
+            mRestoredHotseatSize = c.getInt(3);
+            return true;
+        }
+    }
+}
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java
index d9b1a3f..faecc06 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTask.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java
@@ -1,7 +1,10 @@
 package com.android.launcher3.model;
 
+import static com.android.launcher3.LauncherSettings.Settings.EXTRA_VALUE;
+import static com.android.launcher3.Utilities.getPointString;
+import static com.android.launcher3.Utilities.parsePoint;
+
 import android.content.ComponentName;
-import android.content.ContentProviderOperation;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
@@ -9,29 +12,34 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
 import android.graphics.Point;
-import android.net.Uri;
-import android.text.TextUtils;
 import android.util.Log;
+import android.util.SparseArray;
+
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.LauncherModel;
-import com.android.launcher3.LauncherProvider;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.LauncherSettings.Settings;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.PackageInstallerCompat;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.provider.LauncherDbUtils;
+import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
 import com.android.launcher3.util.GridOccupancy;
-import com.android.launcher3.util.LongArrayMap;
+import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.IntSparseArrayMap;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashSet;
-import java.util.Locale;
+
+import androidx.annotation.VisibleForTesting;
 
 /**
  * This class takes care of shrinking the workspace (by maximum of one row and one column), as a
@@ -39,8 +47,6 @@
  */
 public class GridSizeMigrationTask {
 
-    public static boolean ENABLED = Utilities.ATLEAST_NOUGAT;
-
     private static final String TAG = "GridSizeMigrationTask";
     private static final boolean DEBUG = true;
 
@@ -55,13 +61,13 @@
     private static final float WT_WIDGET_FACTOR = 0.6f;
     private static final float WT_FOLDER_FACTOR = 0.5f;
 
-    private final Context mContext;
-    private final InvariantDeviceProfile mIdp;
+    protected final SQLiteDatabase mDb;
+    protected final Context mContext;
 
-    private final ContentValues mTempValues = new ContentValues();
-    protected final ArrayList<Long> mEntryToRemove = new ArrayList<>();
-    private final ArrayList<ContentProviderOperation> mUpdateOperations = new ArrayList<>();
+    protected final IntArray mEntryToRemove = new IntArray();
     protected final ArrayList<DbEntry> mCarryOver = new ArrayList<>();
+
+    private final SparseArray<ContentValues> mUpdateOperations = new SparseArray<>();
     private final HashSet<String> mValidPackages;
 
     private final int mSrcX, mSrcY;
@@ -71,11 +77,11 @@
     private final int mSrcHotseatSize;
     private final int mDestHotseatSize;
 
-    protected GridSizeMigrationTask(Context context, InvariantDeviceProfile idp,
+    protected GridSizeMigrationTask(Context context, SQLiteDatabase db,
             HashSet<String> validPackages, Point sourceSize, Point targetSize) {
         mContext = context;
+        mDb = db;
         mValidPackages = validPackages;
-        mIdp = idp;
 
         mSrcX = sourceSize.x;
         mSrcY = sourceSize.y;
@@ -90,11 +96,10 @@
         mSrcHotseatSize = mDestHotseatSize = -1;
     }
 
-    protected GridSizeMigrationTask(Context context,
-            InvariantDeviceProfile idp, HashSet<String> validPackages,
-            int srcHotseatSize, int destHotseatSize) {
+    protected GridSizeMigrationTask(Context context, SQLiteDatabase db,
+            HashSet<String> validPackages, int srcHotseatSize, int destHotseatSize) {
         mContext = context;
-        mIdp = idp;
+        mDb = db;
         mValidPackages = validPackages;
 
         mSrcHotseatSize = srcHotseatSize;
@@ -108,24 +113,26 @@
 
     /**
      * Applied all the pending DB operations
+     *
      * @return true if any DB operation was commited.
      */
     private boolean applyOperations() throws Exception {
         // Update items
-        if (!mUpdateOperations.isEmpty()) {
-            mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY, mUpdateOperations);
+        int updateCount = mUpdateOperations.size();
+        for (int i = 0; i < updateCount; i++) {
+            mDb.update(Favorites.TABLE_NAME, mUpdateOperations.valueAt(i),
+                    "_id=" + mUpdateOperations.keyAt(i), null);
         }
 
         if (!mEntryToRemove.isEmpty()) {
             if (DEBUG) {
-                Log.d(TAG, "Removing items: " + TextUtils.join(", ", mEntryToRemove));
+                Log.d(TAG, "Removing items: " + mEntryToRemove.toConcatString());
             }
-            mContext.getContentResolver().delete(LauncherSettings.Favorites.CONTENT_URI,
-                    Utilities.createDbSelectionQuery(
-                            LauncherSettings.Favorites._ID, mEntryToRemove), null);
+            mDb.delete(Favorites.TABLE_NAME, Utilities.createDbSelectionQuery(
+                    Favorites._ID, mEntryToRemove), null);
         }
 
-        return !mUpdateOperations.isEmpty() || !mEntryToRemove.isEmpty();
+        return updateCount > 0 || !mEntryToRemove.isEmpty();
     }
 
     /**
@@ -134,14 +141,12 @@
      * entries is more than what can fit in the new hotseat, we drop the entries with least weight.
      * For weight calculation {@see #WT_SHORTCUT}, {@see #WT_APPLICATION}
      * & {@see #WT_FOLDER_FACTOR}.
+     *
      * @return true if any DB change was made
      */
     protected boolean migrateHotseat() throws Exception {
         ArrayList<DbEntry> items = loadHotseatEntries();
-
-        int requiredCount = FeatureFlags.NO_ALL_APPS_ICON ? mDestHotseatSize : mDestHotseatSize - 1;
-
-        while (items.size() > requiredCount) {
+        while (items.size() > mDestHotseatSize) {
             // Pick the center item by default.
             DbEntry toRemove = items.get(items.size() / 2);
 
@@ -171,24 +176,29 @@
             }
 
             newScreenId++;
-            if (!FeatureFlags.NO_ALL_APPS_ICON && mIdp.isAllAppsButtonRank(newScreenId)) {
-                newScreenId++;
-            }
         }
 
         return applyOperations();
     }
 
+    @VisibleForTesting
+    static IntArray getWorkspaceScreenIds(SQLiteDatabase db) {
+        return LauncherDbUtils.queryIntArray(db, Favorites.TABLE_NAME, Favorites.SCREEN,
+                Favorites.CONTAINER + " = " + Favorites.CONTAINER_DESKTOP,
+                Favorites.SCREEN, Favorites.SCREEN);
+    }
+
     /**
      * @return true if any DB change was made
      */
     protected boolean migrateWorkspace() throws Exception {
-        ArrayList<Long> allScreens = LauncherModel.loadWorkspaceScreensDb(mContext);
+        IntArray allScreens = getWorkspaceScreenIds(mDb);
         if (allScreens.isEmpty()) {
             throw new Exception("Unable to get workspace screens");
         }
 
-        for (long screenId : allScreens) {
+        for (int i = 0; i < allScreens.size(); i++) {
+            int screenId = allScreens.get(i);
             if (DEBUG) {
                 Log.d(TAG, "Migrating " + screenId);
             }
@@ -196,7 +206,7 @@
         }
 
         if (!mCarryOver.isEmpty()) {
-            LongArrayMap<DbEntry> itemMap = new LongArrayMap<>();
+            IntSparseArrayMap<DbEntry> itemMap = new IntSparseArrayMap<>();
             for (DbEntry e : mCarryOver) {
                 itemMap.put(e.id, e);
             }
@@ -211,12 +221,10 @@
                         new GridOccupancy(mTrgX, mTrgY), deepCopy(mCarryOver), 0, true);
                 placement.find();
                 if (placement.finalPlacedItems.size() > 0) {
-                    long newScreenId = LauncherSettings.Settings.call(
+                    int newScreenId = LauncherSettings.Settings.call(
                             mContext.getContentResolver(),
                             LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
-                            .getLong(LauncherSettings.Settings.EXTRA_VALUE);
-
-                    allScreens.add(newScreenId);
+                            .getInt(EXTRA_VALUE);
                     for (DbEntry item : placement.finalPlacedItems) {
                         if (!mCarryOver.remove(itemMap.get(item.id))) {
                             throw new Exception("Unable to find matching items");
@@ -229,18 +237,6 @@
                 }
 
             } while (!mCarryOver.isEmpty());
-
-            // Update screens
-            final Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
-            mUpdateOperations.add(ContentProviderOperation.newDelete(uri).build());
-            int count = allScreens.size();
-            for (int i = 0; i < count; i++) {
-                ContentValues v = new ContentValues();
-                long screenId = allScreens.get(i);
-                v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
-                v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
-                mUpdateOperations.add(ContentProviderOperation.newInsert(uri).withValues(v).build());
-            }
         }
         return applyOperations();
     }
@@ -255,7 +251,7 @@
      *   3) If all those items from the above list can be placed on this screen, place them
      *      (otherwise they are placed on a new screen).
      */
-    protected void migrateScreen(long screenId) {
+    protected void migrateScreen(int screenId) {
         // If we are migrating the first screen, do not touch the first row.
         int startY = (FeatureFlags.QSB_ON_FIRST_SCREEN && screenId == Workspace.FIRST_SCREEN_ID)
                 ? 1 : 0;
@@ -282,9 +278,11 @@
             for (int y = mSrcY - 1; y >= startY; y--) {
                 // Use a deep copy when trying out a particular combination as it can change
                 // the underlying object.
-                ArrayList<DbEntry> itemsOnScreen = tryRemove(x, y, startY, deepCopy(items), outLoss);
+                ArrayList<DbEntry> itemsOnScreen = tryRemove(x, y, startY, deepCopy(items),
+                        outLoss);
 
-                if ((outLoss[0] < removeWt) || ((outLoss[0] == removeWt) && (outLoss[1] < moveWt))) {
+                if ((outLoss[0] < removeWt) || ((outLoss[0] == removeWt) && (outLoss[1]
+                        < moveWt))) {
                     removeWt = outLoss[0];
                     moveWt = outLoss[1];
                     removedCol = mShouldRemoveX ? x : removedCol;
@@ -308,7 +306,7 @@
                     removedRow, removedCol, screenId));
         }
 
-        LongArrayMap<DbEntry> itemMap = new LongArrayMap<>();
+        IntSparseArrayMap<DbEntry> itemMap = new IntSparseArrayMap<>();
         for (DbEntry e : deepCopy(items)) {
             itemMap.put(e.id, e);
         }
@@ -356,15 +354,14 @@
      * Updates an item in the DB.
      */
     protected void update(DbEntry item) {
-        mTempValues.clear();
-        item.addToContentValues(mTempValues);
-        mUpdateOperations.add(ContentProviderOperation
-                .newUpdate(LauncherSettings.Favorites.getContentUri(item.id))
-                .withValues(mTempValues).build());
+        ContentValues values = new ContentValues();
+        item.addToContentValues(values);
+        mUpdateOperations.put(item.id, values);
     }
 
     /**
      * Tries the remove the provided row and column.
+     *
      * @param items all the items on the screen under operation
      * @param outLoss array of size 2. The first entry is filled with weight loss, and the second
      * with the overall item movement.
@@ -440,6 +437,7 @@
 
         /**
          * Recursively finds a placement for the provided items.
+         *
          * @param index the position in {@link #itemsToPlace} to start looking at.
          * @param weightLoss total weight loss upto this point
          * @param moveCost total move cost upto this point
@@ -552,7 +550,8 @@
                     for (int x = 0; x < mTrgX; x++) {
                         if (!occupied.cells[x][y]) {
                             int dist = ignoreMove ? 0 :
-                                ((me.cellX - x) * (me.cellX - x) + (me.cellY - y) * (me.cellY - y));
+                                    ((me.cellX - x) * (me.cellX - x) + (me.cellY - y) * (me.cellY
+                                            - y));
                             if (dist < newDistance) {
                                 newX = x;
                                 newY = y;
@@ -603,13 +602,13 @@
     }
 
     private ArrayList<DbEntry> loadHotseatEntries() {
-        Cursor c =  mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
+        Cursor c =  queryWorkspace(
                 new String[]{
                         Favorites._ID,                  // 0
                         Favorites.ITEM_TYPE,            // 1
                         Favorites.INTENT,               // 2
                         Favorites.SCREEN},              // 3
-                Favorites.CONTAINER + " = " + Favorites.CONTAINER_HOTSEAT, null, null, null);
+                Favorites.CONTAINER + " = " + Favorites.CONTAINER_HOTSEAT);
 
         final int indexId = c.getColumnIndexOrThrow(Favorites._ID);
         final int indexItemType = c.getColumnIndexOrThrow(Favorites.ITEM_TYPE);
@@ -619,9 +618,9 @@
         ArrayList<DbEntry> entries = new ArrayList<>();
         while (c.moveToNext()) {
             DbEntry entry = new DbEntry();
-            entry.id = c.getLong(indexId);
+            entry.id = c.getInt(indexId);
             entry.itemType = c.getInt(indexItemType);
-            entry.screenId = c.getLong(indexScreen);
+            entry.screenId = c.getInt(indexScreen);
 
             if (entry.screenId >= mSrcHotseatSize) {
                 mEntryToRemove.add(entry.id);
@@ -667,7 +666,7 @@
     /**
      * Loads entries for a particular screen id.
      */
-    protected ArrayList<DbEntry> loadWorkspaceEntries(long screen) {
+    protected ArrayList<DbEntry> loadWorkspaceEntries(int screen) {
         Cursor c = queryWorkspace(
                 new String[]{
                         Favorites._ID,                  // 0
@@ -695,7 +694,7 @@
         ArrayList<DbEntry> entries = new ArrayList<>();
         while (c.moveToNext()) {
             DbEntry entry = new DbEntry();
-            entry.id = c.getLong(indexId);
+            entry.id = c.getInt(indexId);
             entry.itemType = c.getInt(indexItemType);
             entry.cellX = c.getInt(indexCellX);
             entry.cellY = c.getInt(indexCellY);
@@ -768,7 +767,7 @@
     /**
      * @return the number of valid items in the folder.
      */
-    private int getFolderItemsCount(long folderId) {
+    private int getFolderItemsCount(int folderId) {
         Cursor c = queryWorkspace(
                 new String[]{Favorites._ID, Favorites.INTENT},
                 Favorites.CONTAINER + " = " + folderId);
@@ -779,7 +778,7 @@
                 verifyIntent(c.getString(1));
                 total++;
             } catch (Exception e) {
-                mEntryToRemove.add(c.getLong(0));
+                mEntryToRemove.add(c.getInt(0));
             }
         }
         c.close();
@@ -787,8 +786,7 @@
     }
 
     protected Cursor queryWorkspace(String[] columns, String where) {
-        return mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
-                columns, where, null, null, null);
+        return mDb.query(Favorites.TABLE_NAME, columns, where, null, null, null, null);
     }
 
     /**
@@ -817,7 +815,8 @@
 
         public float weight;
 
-        public DbEntry() { }
+        public DbEntry() {
+        }
 
         public DbEntry copy() {
             DbEntry entry = new DbEntry();
@@ -854,11 +853,11 @@
         }
 
         public void addToContentValues(ContentValues values) {
-            values.put(LauncherSettings.Favorites.SCREEN, screenId);
-            values.put(LauncherSettings.Favorites.CELLX, cellX);
-            values.put(LauncherSettings.Favorites.CELLY, cellY);
-            values.put(LauncherSettings.Favorites.SPANX, spanX);
-            values.put(LauncherSettings.Favorites.SPANY, spanY);
+            values.put(Favorites.SCREEN, screenId);
+            values.put(Favorites.CELLX, cellX);
+            values.put(Favorites.CELLY, cellY);
+            values.put(Favorites.SPANX, spanX);
+            values.put(Favorites.SPANY, spanY);
         }
     }
 
@@ -870,15 +869,6 @@
         return dup;
     }
 
-    private static Point parsePoint(String point) {
-        String[] split = point.split(",");
-        return new Point(Integer.parseInt(split[0]), Integer.parseInt(split[1]));
-    }
-
-    private static String getPointString(int x, int y) {
-        return String.format(Locale.ENGLISH, "%d,%d", x, y);
-    }
-
     public static void markForMigration(
             Context context, int gridX, int gridY, int hotseatSize) {
         Utilities.getPrefs(context).edit()
@@ -889,6 +879,7 @@
 
     /**
      * Migrates the workspace and hotseat in case their sizes changed.
+     *
      * @return false if the migration failed.
      */
     public static boolean migrateGridIfNeeded(Context context) {
@@ -898,38 +889,50 @@
         String gridSizeString = getPointString(idp.numColumns, idp.numRows);
 
         if (gridSizeString.equals(prefs.getString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, "")) &&
-                idp.numHotseatIcons == prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons)) {
+                idp.numHotseatIcons == prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT,
+                        idp.numHotseatIcons)) {
             // Skip if workspace and hotseat sizes have not changed.
             return true;
         }
 
         long migrationStartTime = System.currentTimeMillis();
-        try {
+        try (SQLiteTransaction transaction = (SQLiteTransaction) Settings.call(
+                context.getContentResolver(), Settings.METHOD_NEW_TRANSACTION)
+                .getBinder(Settings.EXTRA_VALUE)) {
+
+            int srcHotseatCount = prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT,
+                    idp.numHotseatIcons);
+            Point sourceSize = parsePoint(prefs.getString(
+                    KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString));
+
             boolean dbChanged = false;
 
+            GridBackupTable backupTable = new GridBackupTable(context, transaction.getDb(),
+                    srcHotseatCount, sourceSize.x, sourceSize.y);
+            if (backupTable.backupOrRestoreAsNeeded()) {
+                dbChanged = true;
+                srcHotseatCount = backupTable.getRestoreHotseatAndGridSize(sourceSize);
+            }
+
             HashSet<String> validPackages = getValidPackages(context);
             // Hotseat
-            int srcHotseatCount = prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons);
             if (srcHotseatCount != idp.numHotseatIcons) {
                 // Migrate hotseat.
-
-                dbChanged = new GridSizeMigrationTask(context, LauncherAppState.getIDP(context),
+                dbChanged = new GridSizeMigrationTask(context, transaction.getDb(),
                         validPackages, srcHotseatCount, idp.numHotseatIcons).migrateHotseat();
             }
 
             // Grid size
             Point targetSize = new Point(idp.numColumns, idp.numRows);
-            Point sourceSize = parsePoint(prefs.getString(
-                    KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString));
-
-            if (new MultiStepMigrationTask(validPackages, context).migrate(sourceSize, targetSize)) {
+            if (new MultiStepMigrationTask(validPackages, context, transaction.getDb())
+                    .migrate(sourceSize, targetSize)) {
                 dbChanged = true;
             }
 
             if (dbChanged) {
                 // Make sure we haven't removed everything.
                 final Cursor c = context.getContentResolver().query(
-                        LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
+                        Favorites.CONTENT_URI, null, null, null, null);
                 boolean hasData = c.moveToNext();
                 c.close();
                 if (!hasData) {
@@ -937,6 +940,8 @@
                 }
             }
 
+            transaction.commit();
+            Settings.call(context.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
             return true;
         } catch (Exception e) {
             Log.e(TAG, "Error during grid migration", e);
@@ -972,22 +977,29 @@
 
     /**
      * Removes any broken item from the hotseat.
+     *
      * @return a map with occupied hotseat position set to non-null value.
      */
-    public static LongArrayMap<Object> removeBrokenHotseatItems(Context context) throws Exception {
-        GridSizeMigrationTask task = new GridSizeMigrationTask(
-                context, LauncherAppState.getIDP(context), getValidPackages(context),
-                Integer.MAX_VALUE, Integer.MAX_VALUE);
+    public static IntSparseArrayMap<Object> removeBrokenHotseatItems(Context context)
+            throws Exception {
+        try (SQLiteTransaction transaction = (SQLiteTransaction) Settings.call(
+                context.getContentResolver(), Settings.METHOD_NEW_TRANSACTION)
+                .getBinder(Settings.EXTRA_VALUE)) {
+            GridSizeMigrationTask task = new GridSizeMigrationTask(
+                    context, transaction.getDb(), getValidPackages(context),
+                    Integer.MAX_VALUE, Integer.MAX_VALUE);
 
-        // Load all the valid entries
-        ArrayList<DbEntry> items = task.loadHotseatEntries();
-        // Delete any entry marked for deletion by above load.
-        task.applyOperations();
-        LongArrayMap<Object> positions = new LongArrayMap<>();
-        for (DbEntry item : items) {
-            positions.put(item.screenId, item);
+            // Load all the valid entries
+            ArrayList<DbEntry> items = task.loadHotseatEntries();
+            // Delete any entry marked for deletion by above load.
+            task.applyOperations();
+            IntSparseArrayMap<Object> positions = new IntSparseArrayMap<>();
+            for (DbEntry item : items) {
+                positions.put(item.screenId, item);
+            }
+            transaction.commit();
+            return positions;
         }
-        return positions;
     }
 
     /**
@@ -996,10 +1008,13 @@
     protected static class MultiStepMigrationTask {
         private final HashSet<String> mValidPackages;
         private final Context mContext;
+        private final SQLiteDatabase mDb;
 
-        public MultiStepMigrationTask(HashSet<String> validPackages, Context context) {
+        public MultiStepMigrationTask(HashSet<String> validPackages, Context context,
+                SQLiteDatabase db) {
             mValidPackages = validPackages;
             mContext = context;
+            mDb = db;
         }
 
         public boolean migrate(Point sourceSize, Point targetSize) throws Exception {
@@ -1035,7 +1050,7 @@
         }
 
         protected boolean runStepTask(Point sourceSize, Point nextSize) throws Exception {
-            return new GridSizeMigrationTask(mContext, LauncherAppState.getIDP(mContext),
+            return new GridSizeMigrationTask(mContext, mDb,
                     mValidPackages, sourceSize, nextSize).migrateWorkspace();
         }
     }
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 6378ea1..1a03b77 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -22,9 +22,9 @@
 import android.content.Intent;
 import android.content.Intent.ShortcutIconResource;
 import android.content.pm.LauncherActivityInfo;
+import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.database.CursorWrapper;
-import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.os.UserHandle;
 import android.provider.BaseColumns;
@@ -33,27 +33,26 @@
 import android.util.LongSparseArray;
 
 import com.android.launcher3.AppInfo;
-import com.android.launcher3.IconCache;
+import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.compat.LauncherAppsCompat;
-import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.graphics.BitmapInfo;
-import com.android.launcher3.graphics.LauncherIcons;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.util.ContentWriter;
 import com.android.launcher3.util.GridOccupancy;
-import com.android.launcher3.util.LongArrayMap;
+import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.IntSparseArrayMap;
 
 import java.net.URISyntaxException;
 import java.security.InvalidParameterException;
-import java.util.ArrayList;
 
 /**
  * Extension of {@link Cursor} with utility methods for workspace loading.
@@ -65,13 +64,13 @@
     public final LongSparseArray<UserHandle> allUsers = new LongSparseArray<>();
 
     private final Context mContext;
-    private final UserManagerCompat mUserManager;
+    private final PackageManager mPM;
     private final IconCache mIconCache;
     private final InvariantDeviceProfile mIDP;
 
-    private final ArrayList<Long> itemsToRemove = new ArrayList<>();
-    private final ArrayList<Long> restoredRows = new ArrayList<>();
-    private final LongArrayMap<GridOccupancy> occupied = new LongArrayMap<>();
+    private final IntArray itemsToRemove = new IntArray();
+    private final IntArray restoredRows = new IntArray();
+    private final IntSparseArrayMap<GridOccupancy> occupied = new IntSparseArrayMap<>();
 
     private final int iconPackageIndex;
     private final int iconResourceIndex;
@@ -91,8 +90,8 @@
     // Properties loaded per iteration
     public long serialNumber;
     public UserHandle user;
-    public long id;
-    public long container;
+    public int id;
+    public int container;
     public int itemType;
     public int restoreFlag;
 
@@ -101,7 +100,7 @@
         mContext = app.getContext();
         mIconCache = app.getIconCache();
         mIDP = app.getInvariantDeviceProfile();
-        mUserManager = UserManagerCompat.getInstance(mContext);
+        mPM = mContext.getPackageManager();
 
         // Init column indices
         iconIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
@@ -127,7 +126,7 @@
             // Load common properties.
             itemType = getInt(itemTypeIndex);
             container = getInt(containerIndex);
-            id = getLong(idIndex);
+            id = getInt(idIndex);
             serialNumber = getInt(profileIdIndex);
             user = allUsers.get(serialNumber);
             restoreFlag = getInt(restoredIndex);
@@ -146,15 +145,15 @@
         }
     }
 
-    public ShortcutInfo loadSimpleShortcut() {
-        final ShortcutInfo info = new ShortcutInfo();
+    public WorkspaceItemInfo loadSimpleWorkspaceItem() {
+        final WorkspaceItemInfo info = new WorkspaceItemInfo();
         // Non-app shortcuts are only supported for current user.
         info.user = user;
         info.itemType = itemType;
         info.title = getTitle();
         // the fallback icon
         if (!loadIcon(info)) {
-            mIconCache.getDefaultIcon(info.user).applyTo(info);
+            info.applyFrom(mIconCache.getDefaultIcon(info.user));
         }
 
         // TODO: If there's an explicit component and we can't install that, delete it.
@@ -165,7 +164,16 @@
     /**
      * Loads the icon from the cursor and updates the {@param info} if the icon is an app resource.
      */
-    protected boolean loadIcon(ShortcutInfo info) {
+    protected boolean loadIcon(WorkspaceItemInfo info) {
+        try (LauncherIcons li = LauncherIcons.obtain(mContext)) {
+            return loadIcon(info, li);
+        }
+    }
+
+    /**
+     * Loads the icon from the cursor and updates the {@param info} if the icon is an app resource.
+     */
+    protected boolean loadIcon(WorkspaceItemInfo info, LauncherIcons li) {
         if (itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
             String packageName = getString(iconPackageIndex);
             String resourceName = getString(iconResourceIndex);
@@ -173,11 +181,9 @@
                 info.iconResource = new ShortcutIconResource();
                 info.iconResource.packageName = packageName;
                 info.iconResource.resourceName = resourceName;
-                LauncherIcons li = LauncherIcons.obtain(mContext);
                 BitmapInfo iconInfo = li.createIconBitmap(info.iconResource);
-                li.recycle();
                 if (iconInfo != null) {
-                    iconInfo.applyTo(info);
+                    info.applyFrom(iconInfo);
                     return true;
                 }
             }
@@ -185,11 +191,11 @@
 
         // Failed to load from resource, try loading from DB.
         byte[] data = getBlob(iconIndex);
-        try (LauncherIcons li = LauncherIcons.obtain(mContext)) {
-            li.createIconBitmap(BitmapFactory.decodeByteArray(data, 0, data.length)).applyTo(info);
+        try {
+            info.applyFrom(li.createIconBitmap(BitmapFactory.decodeByteArray(data, 0, data.length)));
             return true;
         } catch (Exception e) {
-            Log.e(TAG, "Failed to load icon for info " + info, e);
+            Log.e(TAG, "Failed to decode byte array for info " + info, e);
             return false;
         }
     }
@@ -203,11 +209,11 @@
     }
 
     /**
-     * Make an ShortcutInfo object for a restored application or shortcut item that points
+     * Make an WorkspaceItemInfo object for a restored application or shortcut item that points
      * to a package that is not yet installed on the system.
      */
-    public ShortcutInfo getRestoredItemInfo(Intent intent) {
-        final ShortcutInfo info = new ShortcutInfo();
+    public WorkspaceItemInfo getRestoredItemInfo(Intent intent) {
+        final WorkspaceItemInfo info = new WorkspaceItemInfo();
         info.user = user;
         info.intent = intent;
 
@@ -216,12 +222,12 @@
             mIconCache.getTitleAndIcon(info, false /* useLowResIcon */);
         }
 
-        if (hasRestoreFlag(ShortcutInfo.FLAG_RESTORED_ICON)) {
+        if (hasRestoreFlag(WorkspaceItemInfo.FLAG_RESTORED_ICON)) {
             String title = getTitle();
             if (!TextUtils.isEmpty(title)) {
                 info.title = Utilities.trim(title);
             }
-        } else if  (hasRestoreFlag(ShortcutInfo.FLAG_AUTOINSTALL_ICON)) {
+        } else if  (hasRestoreFlag(WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON)) {
             if (TextUtils.isEmpty(info.title)) {
                 info.title = getTitle();
             }
@@ -229,16 +235,16 @@
             throw new InvalidParameterException("Invalid restoreType " + restoreFlag);
         }
 
-        info.contentDescription = mUserManager.getBadgedLabelForUser(info.title, info.user);
+        info.contentDescription = mPM.getUserBadgedLabel(info.title, info.user);
         info.itemType = itemType;
         info.status = restoreFlag;
         return info;
     }
 
     /**
-     * Make an ShortcutInfo object for a shortcut that is an application.
+     * Make an WorkspaceItemInfo object for a shortcut that is an application.
      */
-    public ShortcutInfo getAppShortcutInfo(
+    public WorkspaceItemInfo getAppShortcutInfo(
             Intent intent, boolean allowMissingTarget, boolean useLowResIcon) {
         if (user == null) {
             Log.d(TAG, "Null user found in getShortcutInfo");
@@ -261,7 +267,7 @@
             return null;
         }
 
-        final ShortcutInfo info = new ShortcutInfo();
+        final WorkspaceItemInfo info = new WorkspaceItemInfo();
         info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
         info.user = user;
         info.intent = newIntent;
@@ -285,7 +291,7 @@
             info.title = componentName.getClassName();
         }
 
-        info.contentDescription = mUserManager.getBadgedLabelForUser(info.title, info.user);
+        info.contentDescription = mPM.getUserBadgedLabel(info.title, info.user);
         return info;
     }
 
@@ -294,7 +300,7 @@
      */
     public ContentWriter updater() {
        return new ContentWriter(mContext, new ContentWriter.CommitParams(
-               BaseColumns._ID + "= ?", new String[]{Long.toString(id)}));
+               BaseColumns._ID + "= ?", new String[]{Integer.toString(id)}));
     }
 
     /**
@@ -374,7 +380,7 @@
      * otherwise marks it for deletion.
      */
     public void checkAndAddItem(ItemInfo info, BgDataModel dataModel) {
-        if (checkItemPlacement(info, dataModel.workspaceScreens)) {
+        if (checkItemPlacement(info)) {
             dataModel.addItem(mContext, info, false);
         } else {
             markDeleted("Item position overlap");
@@ -384,20 +390,11 @@
     /**
      * check & update map of what's occupied; used to discard overlapping/invalid items
      */
-    protected boolean checkItemPlacement(ItemInfo item, ArrayList<Long> workspaceScreens) {
-        long containerIndex = item.screenId;
+    protected boolean checkItemPlacement(ItemInfo item) {
+        int containerIndex = item.screenId;
         if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
-            // Return early if we detect that an item is under the hotseat button
-            if (!FeatureFlags.NO_ALL_APPS_ICON &&
-                    mIDP.isAllAppsButtonRank((int) item.screenId)) {
-                Log.e(TAG, "Error loading shortcut into hotseat " + item
-                        + " into position (" + item.screenId + ":" + item.cellX + ","
-                        + item.cellY + ") occupied by all apps");
-                return false;
-            }
-
             final GridOccupancy hotseatOccupancy =
-                    occupied.get((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT);
+                    occupied.get(LauncherSettings.Favorites.CONTAINER_HOTSEAT);
 
             if (item.screenId >= mIDP.numHotseatIcons) {
                 Log.e(TAG, "Error loading shortcut " + item
@@ -414,21 +411,16 @@
                             + item.cellY + ") already occupied");
                     return false;
                 } else {
-                    hotseatOccupancy.cells[(int) item.screenId][0] = true;
+                    hotseatOccupancy.cells[item.screenId][0] = true;
                     return true;
                 }
             } else {
                 final GridOccupancy occupancy = new GridOccupancy(mIDP.numHotseatIcons, 1);
-                occupancy.cells[(int) item.screenId][0] = true;
-                occupied.put((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT, occupancy);
+                occupancy.cells[item.screenId][0] = true;
+                occupied.put(LauncherSettings.Favorites.CONTAINER_HOTSEAT, occupancy);
                 return true;
             }
-        } else if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
-            if (!workspaceScreens.contains((Long) item.screenId)) {
-                // The item has an invalid screen id.
-                return false;
-            }
-        } else {
+        } else if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
             // Skip further checking if it is not the hotseat or workspace container
             return true;
         }
diff --git a/src/com/android/launcher3/model/LoaderResults.java b/src/com/android/launcher3/model/LoaderResults.java
deleted file mode 100644
index 0fd9b73..0000000
--- a/src/com/android/launcher3/model/LoaderResults.java
+++ /dev/null
@@ -1,387 +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;
-
-import android.os.Looper;
-import android.util.Log;
-
-import com.android.launcher3.AllAppsList;
-import com.android.launcher3.AppInfo;
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherAppWidgetInfo;
-import com.android.launcher3.LauncherModel.Callbacks;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.MainThreadExecutor;
-import com.android.launcher3.PagedView;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.LooperIdleLock;
-import com.android.launcher3.util.MultiHashMap;
-import com.android.launcher3.util.ViewOnDrawExecutor;
-import com.android.launcher3.widget.WidgetListRowEntry;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-import java.util.concurrent.Executor;
-
-/**
- * Helper class to handle results of {@link com.android.launcher3.model.LoaderTask}.
- */
-public class LoaderResults {
-
-    private static final String TAG = "LoaderResults";
-    private static final long INVALID_SCREEN_ID = -1L;
-    private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons
-
-    private final Executor mUiExecutor;
-
-    private final LauncherAppState mApp;
-    private final BgDataModel mBgDataModel;
-    private final AllAppsList mBgAllAppsList;
-    private final int mPageToBindFirst;
-
-    private final WeakReference<Callbacks> mCallbacks;
-
-    public LoaderResults(LauncherAppState app, BgDataModel dataModel,
-            AllAppsList allAppsList, int pageToBindFirst, WeakReference<Callbacks> callbacks) {
-        mUiExecutor = new MainThreadExecutor();
-        mApp = app;
-        mBgDataModel = dataModel;
-        mBgAllAppsList = allAppsList;
-        mPageToBindFirst = pageToBindFirst;
-        mCallbacks = callbacks == null ? new WeakReference<Callbacks>(null) : callbacks;
-    }
-
-    /**
-     * Binds all loaded data to actual views on the main thread.
-     */
-    public void bindWorkspace() {
-        Runnable r;
-
-        Callbacks callbacks = mCallbacks.get();
-        // Don't use these two variables in any of the callback runnables.
-        // Otherwise we hold a reference to them.
-        if (callbacks == null) {
-            // This launcher has exited and nobody bothered to tell us.  Just bail.
-            Log.w(TAG, "LoaderTask running with no launcher");
-            return;
-        }
-
-        // Save a copy of all the bg-thread collections
-        ArrayList<ItemInfo> workspaceItems = new ArrayList<>();
-        ArrayList<LauncherAppWidgetInfo> appWidgets = new ArrayList<>();
-        final ArrayList<Long> orderedScreenIds = new ArrayList<>();
-
-        synchronized (mBgDataModel) {
-            workspaceItems.addAll(mBgDataModel.workspaceItems);
-            appWidgets.addAll(mBgDataModel.appWidgets);
-            orderedScreenIds.addAll(mBgDataModel.workspaceScreens);
-            mBgDataModel.lastBindId++;
-        }
-
-        final int currentScreen;
-        {
-            int currScreen = mPageToBindFirst != PagedView.INVALID_RESTORE_PAGE
-                    ? mPageToBindFirst : callbacks.getCurrentWorkspaceScreen();
-            if (currScreen >= orderedScreenIds.size()) {
-                // There may be no workspace screens (just hotseat items and an empty page).
-                currScreen = PagedView.INVALID_RESTORE_PAGE;
-            }
-            currentScreen = currScreen;
-        }
-        final boolean validFirstPage = currentScreen >= 0;
-        final long currentScreenId =
-                validFirstPage ? orderedScreenIds.get(currentScreen) : INVALID_SCREEN_ID;
-
-        // 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(currentScreenId, workspaceItems, currentWorkspaceItems,
-                otherWorkspaceItems);
-        filterCurrentWorkspaceItems(currentScreenId, appWidgets, currentAppWidgets,
-                otherAppWidgets);
-        sortWorkspaceItemsSpatially(currentWorkspaceItems);
-        sortWorkspaceItemsSpatially(otherWorkspaceItems);
-
-        // Tell the workspace that we're about to start binding items
-        r = new Runnable() {
-            public void run() {
-                Callbacks callbacks = mCallbacks.get();
-                if (callbacks != null) {
-                    callbacks.clearPendingBinds();
-                    callbacks.startBinding();
-                }
-            }
-        };
-        mUiExecutor.execute(r);
-
-        // Bind workspace screens
-        mUiExecutor.execute(new Runnable() {
-            @Override
-            public void run() {
-                Callbacks callbacks = mCallbacks.get();
-                if (callbacks != null) {
-                    callbacks.bindScreens(orderedScreenIds);
-                }
-            }
-        });
-
-        Executor mainExecutor = mUiExecutor;
-        // Load items on the current page.
-        bindWorkspaceItems(currentWorkspaceItems, currentAppWidgets, 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;
-
-        mainExecutor.execute(new Runnable() {
-            @Override
-            public void run() {
-                Callbacks callbacks = mCallbacks.get();
-                if (callbacks != null) {
-                    callbacks.finishFirstPageBind(
-                            validFirstPage ? (ViewOnDrawExecutor) deferredExecutor : null);
-                }
-            }
-        });
-
-        bindWorkspaceItems(otherWorkspaceItems, otherAppWidgets, deferredExecutor);
-
-        // Tell the workspace that we're done binding items
-        r = new Runnable() {
-            public void run() {
-                Callbacks callbacks = mCallbacks.get();
-                if (callbacks != null) {
-                    callbacks.finishBindingItems();
-                }
-            }
-        };
-        deferredExecutor.execute(r);
-
-        if (validFirstPage) {
-            r = new Runnable() {
-                public void run() {
-                    Callbacks callbacks = mCallbacks.get();
-                    if (callbacks != null) {
-                        // We are loading synchronously, which means, some of the pages will be
-                        // bound after first draw. Inform the callbacks that page binding is
-                        // not complete, and schedule the remaining pages.
-                        if (currentScreen != PagedView.INVALID_RESTORE_PAGE) {
-                            callbacks.onPageBoundSynchronously(currentScreen);
-                        }
-                        callbacks.executeOnNextDraw((ViewOnDrawExecutor) deferredExecutor);
-                    }
-                }
-            };
-            mUiExecutor.execute(r);
-        }
-    }
-
-
-    /** Filters the set of items who are directly or indirectly (via another container) on the
-     * specified screen. */
-    public static <T extends ItemInfo> void filterCurrentWorkspaceItems(long currentScreenId,
-            ArrayList<T> allWorkspaceItems,
-            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();
-            }
-        }
-
-        // 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.
-        Set<Long> itemsOnScreen = new HashSet<>();
-        Collections.sort(allWorkspaceItems, new Comparator<ItemInfo>() {
-            @Override
-            public int compare(ItemInfo lhs, ItemInfo rhs) {
-                return Utilities.longCompare(lhs.container, rhs.container);
-            }
-        });
-        for (T info : allWorkspaceItems) {
-            if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
-                if (info.screenId == currentScreenId) {
-                    currentScreenItems.add(info);
-                    itemsOnScreen.add(info.id);
-                } else {
-                    otherScreenItems.add(info);
-                }
-            } else if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
-                currentScreenItems.add(info);
-                itemsOnScreen.add(info.id);
-            } else {
-                if (itemsOnScreen.contains(info.container)) {
-                    currentScreenItems.add(info);
-                    itemsOnScreen.add(info.id);
-                } else {
-                    otherScreenItems.add(info);
-                }
-            }
-        }
-    }
-
-    /** Sorts the set of items by hotseat, workspace (spatially from top to bottom, left to
-     * right) */
-    private void sortWorkspaceItemsSpatially(ArrayList<ItemInfo> workspaceItems) {
-        final InvariantDeviceProfile profile = mApp.getInvariantDeviceProfile();
-        final int screenCols = profile.numColumns;
-        final int screenCellCount = profile.numColumns * profile.numRows;
-        Collections.sort(workspaceItems, new Comparator<ItemInfo>() {
-            @Override
-            public int compare(ItemInfo lhs, ItemInfo rhs) {
-                if (lhs.container == rhs.container) {
-                    // Within containers, order by their spatial position in that container
-                    switch ((int) lhs.container) {
-                        case LauncherSettings.Favorites.CONTAINER_DESKTOP: {
-                            long lr = (lhs.screenId * screenCellCount +
-                                    lhs.cellY * screenCols + lhs.cellX);
-                            long rr = (rhs.screenId * screenCellCount +
-                                    rhs.cellY * screenCols + rhs.cellX);
-                            return Utilities.longCompare(lr, rr);
-                        }
-                        case LauncherSettings.Favorites.CONTAINER_HOTSEAT: {
-                            // We currently use the screen id as the rank
-                            return Utilities.longCompare(lhs.screenId, rhs.screenId);
-                        }
-                        default:
-                            if (FeatureFlags.IS_DOGFOOD_BUILD) {
-                                throw new RuntimeException("Unexpected container type when " +
-                                        "sorting workspace items.");
-                            }
-                            return 0;
-                    }
-                } else {
-                    // Between containers, order by hotseat, desktop
-                    return Utilities.longCompare(lhs.container, rhs.container);
-                }
-            }
-        });
-    }
-
-    private void bindWorkspaceItems(final ArrayList<ItemInfo> workspaceItems,
-            final ArrayList<LauncherAppWidgetInfo> appWidgets,
-            final Executor executor) {
-
-        // Bind the workspace items
-        int N = workspaceItems.size();
-        for (int i = 0; i < N; i += ITEMS_CHUNK) {
-            final int start = i;
-            final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
-            final Runnable r = new Runnable() {
-                @Override
-                public void run() {
-                    Callbacks callbacks = mCallbacks.get();
-                    if (callbacks != null) {
-                        callbacks.bindItems(workspaceItems.subList(start, start+chunkSize), false);
-                    }
-                }
-            };
-            executor.execute(r);
-        }
-
-        // Bind the widgets, one at a time
-        N = appWidgets.size();
-        for (int i = 0; i < N; i++) {
-            final ItemInfo widget = appWidgets.get(i);
-            final Runnable r = new Runnable() {
-                public void run() {
-                    Callbacks callbacks = mCallbacks.get();
-                    if (callbacks != null) {
-                        callbacks.bindItems(Collections.singletonList(widget), false);
-                    }
-                }
-            };
-            executor.execute(r);
-        }
-    }
-
-    public void bindDeepShortcuts() {
-        final MultiHashMap<ComponentKey, String> shortcutMapCopy;
-        synchronized (mBgDataModel) {
-            shortcutMapCopy = mBgDataModel.deepShortcutMap.clone();
-        }
-        Runnable r = new Runnable() {
-            @Override
-            public void run() {
-                Callbacks callbacks = mCallbacks.get();
-                if (callbacks != null) {
-                    callbacks.bindDeepShortcutMap(shortcutMapCopy);
-                }
-            }
-        };
-        mUiExecutor.execute(r);
-    }
-
-    public void bindAllApps() {
-        // shallow copy
-        @SuppressWarnings("unchecked")
-        final ArrayList<AppInfo> list = (ArrayList<AppInfo>) mBgAllAppsList.data.clone();
-
-        Runnable r = new Runnable() {
-            public void run() {
-                Callbacks callbacks = mCallbacks.get();
-                if (callbacks != null) {
-                    callbacks.bindAllApplications(list);
-                }
-            }
-        };
-        mUiExecutor.execute(r);
-    }
-
-    public void bindWidgets() {
-        final ArrayList<WidgetListRowEntry> widgets =
-                mBgDataModel.widgetsModel.getWidgetsList(mApp.getContext());
-        Runnable r = new Runnable() {
-            public void run() {
-                Callbacks callbacks = mCallbacks.get();
-                if (callbacks != null) {
-                    callbacks.bindAllWidgets(widgets);
-                }
-            }
-        };
-        mUiExecutor.execute(r);
-    }
-
-    public LooperIdleLock newIdleLock(Object lock) {
-        LooperIdleLock idleLock = new LooperIdleLock(lock, Looper.getMainLooper());
-        // If we are not binding, there is no reason to wait for idle.
-        if (mCallbacks.get() == null) {
-            idleLock.queueIdle();
-        }
-        return idleLock;
-    }
-}
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 06da843..0138572 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -19,7 +19,6 @@
 import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
 import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
 import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
-import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
 import static com.android.launcher3.model.LoaderResults.filterCurrentWorkspaceItems;
 
 import android.appwidget.AppWidgetProviderInfo;
@@ -31,7 +30,7 @@
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageInstaller.SessionInfo;
-import android.graphics.Bitmap;
+import android.content.pm.ShortcutInfo;
 import android.os.Handler;
 import android.os.Process;
 import android.os.UserHandle;
@@ -43,14 +42,14 @@
 import com.android.launcher3.AllAppsList;
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.FolderInfo;
-import com.android.launcher3.IconCache;
 import com.android.launcher3.InstallShortcutReceiver;
 import com.android.launcher3.ItemInfo;
+import com.android.launcher3.ItemInfoWithIcon;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.LauncherAppsCompat;
@@ -59,17 +58,20 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIconPreviewVerifier;
-import com.android.launcher3.graphics.LauncherIcons;
+import com.android.launcher3.icons.ComponentWithLabel;
+import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.icons.LauncherActivtiyCachingLogic;
+import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.icons.cache.IconCacheUpdateHandler;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.provider.ImportDataTask;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.LooperIdleLock;
 import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.util.PackageManagerHelper;
-import com.android.launcher3.util.Provider;
 import com.android.launcher3.util.TraceHelper;
 
 import java.util.ArrayList;
@@ -79,6 +81,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.CancellationException;
+import java.util.function.Supplier;
 
 /**
  * Runnable for the thread that loads the contents of the launcher:
@@ -146,9 +149,9 @@
             allItems.addAll(mBgDataModel.workspaceItems);
             allItems.addAll(mBgDataModel.appWidgets);
         }
-        long firstScreen = mBgDataModel.workspaceScreens.isEmpty()
-                ? -1 // In this case, we can still look at the items in the hotseat.
-                : mBgDataModel.workspaceScreens.get(0);
+        // Screen set is never empty
+        final int firstScreen = mBgDataModel.collectWorkspaceScreens().get(0);
+
         filterCurrentWorkspaceItems(firstScreen, allItems, firstScreenItems,
                 new ArrayList<>() /* otherScreenItems are ignored */);
         mFirstScreenBroadcast.sendBroadcasts(mApp.getContext(), firstScreenItems);
@@ -182,7 +185,7 @@
 
             // second step
             TraceHelper.partitionSection(TAG, "step 2.1: loading all apps");
-            loadAllApps();
+            List<LauncherActivityInfo> allActivityList = loadAllApps();
 
             TraceHelper.partitionSection(TAG, "step 2.2: Binding all apps");
             verifyNotStopped();
@@ -190,7 +193,11 @@
 
             verifyNotStopped();
             TraceHelper.partitionSection(TAG, "step 2.3: Update icon cache");
-            updateIconCache();
+            IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler();
+            setIgnorePackages(updateHandler);
+            updateHandler.updateIcons(allActivityList,
+                    new LauncherActivtiyCachingLogic(mApp.getIconCache()),
+                    mApp.getModel()::onPackageIconsUpdated);
 
             // Take a break
             TraceHelper.partitionSection(TAG, "step 2 completed, wait for idle");
@@ -212,12 +219,21 @@
 
             // fourth step
             TraceHelper.partitionSection(TAG, "step 4.1: loading widgets");
-            mBgDataModel.widgetsModel.update(mApp, null);
+            List<ComponentWithLabel> allWidgetsList = mBgDataModel.widgetsModel.update(mApp, null);
 
             verifyNotStopped();
             TraceHelper.partitionSection(TAG, "step 4.2: Binding widgets");
             mResults.bindWidgets();
 
+            verifyNotStopped();
+            TraceHelper.partitionSection(TAG, "step 4.3: Update icon cache");
+            updateHandler.updateIcons(allWidgetsList, new ComponentCachingLogic(mApp.getContext()),
+                    mApp.getModel()::onWidgetLabelsUpdated);
+
+            verifyNotStopped();
+            TraceHelper.partitionSection(TAG, "step 5: Finish icon cache update");
+            updateHandler.finish();
+
             transaction.commit();
         } catch (CancellationException e) {
             // Loader stopped, ignore
@@ -247,8 +263,7 @@
             clearDb = true;
         }
 
-        if (!clearDb && GridSizeMigrationTask.ENABLED &&
-                !GridSizeMigrationTask.migrateGridIfNeeded(context)) {
+        if (!clearDb && !GridSizeMigrationTask.migrateGridIfNeeded(context)) {
             // Migration failed. Clear workspace.
             clearDb = true;
         }
@@ -269,9 +284,8 @@
             final HashMap<String, SessionInfo> installingPkgs =
                     mPackageInstaller.updateAndGetActiveSessionCache();
             mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs);
-            mBgDataModel.workspaceScreens.addAll(LauncherModel.loadWorkspaceScreensDb(context));
 
-            Map<ShortcutKey, ShortcutInfoCompat> shortcutKeyToPinnedShortcuts = new HashMap<>();
+            Map<ShortcutKey, ShortcutInfo> shortcutKeyToPinnedShortcuts = new HashMap<>();
             final LoaderCursor c = new LoaderCursor(contentResolver.query(
                     LauncherSettings.Favorites.CONTENT_URI, null, null, null, null), mApp);
 
@@ -303,10 +317,10 @@
 
                     // We can only query for shortcuts when the user is unlocked.
                     if (userUnlocked) {
-                        List<ShortcutInfoCompat> pinnedShortcuts =
+                        List<ShortcutInfo> pinnedShortcuts =
                                 mShortcutManager.queryForPinnedShortcuts(null, user);
                         if (mShortcutManager.wasLastCallSuccess()) {
-                            for (ShortcutInfoCompat shortcut : pinnedShortcuts) {
+                            for (ShortcutInfo shortcut : pinnedShortcuts) {
                                 shortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut),
                                         shortcut);
                             }
@@ -320,13 +334,11 @@
                     unlockedUsers.put(serialNo, userUnlocked);
                 }
 
-                ShortcutInfo info;
+                WorkspaceItemInfo info;
                 LauncherAppWidgetInfo appWidgetInfo;
                 Intent intent;
                 String targetPkg;
 
-                FolderIconPreviewVerifier verifier =
-                        new FolderIconPreviewVerifier(mApp.getInvariantDeviceProfile());
                 while (!mStopped && c.moveToNext()) {
                     try {
                         if (c.user == null) {
@@ -347,17 +359,17 @@
                             }
 
                             int disabledState = quietMode.get(c.serialNumber) ?
-                                    ShortcutInfo.FLAG_DISABLED_QUIET_USER : 0;
+                                    WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER : 0;
                             ComponentName cn = intent.getComponent();
                             targetPkg = cn == null ? intent.getPackage() : cn.getPackageName();
 
-                            if (!Process.myUserHandle().equals(c.user)) {
+                            if (allUsers.indexOfValue(c.user) < 0) {
                                 if (c.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
-                                    c.markDeleted("Legacy shortcuts are only allowed for default user");
+                                    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 managed profile not supported");
+                                    c.markDeleted("Restore from other profiles not supported");
                                     continue;
                                 }
                             }
@@ -381,24 +393,16 @@
                                     // no special handling necessary for this item
                                     c.markRestored();
                                 } else {
-                                    if (c.hasRestoreFlag(ShortcutInfo.FLAG_AUTOINSTALL_ICON)) {
-                                        // We allow auto install apps to have their intent
-                                        // updated after an install.
-                                        intent = pmHelper.getAppLaunchIntent(targetPkg, c.user);
-                                        if (intent != null) {
-                                            c.restoreFlag = 0;
-                                            c.updater().put(
-                                                    LauncherSettings.Favorites.INTENT,
-                                                    intent.toUri(0)).commit();
-                                            cn = intent.getComponent();
-                                        } else {
-                                            c.markDeleted("Unable to find a launch target");
-                                            continue;
-                                        }
+                                    // Gracefully try to find a fallback activity.
+                                    intent = pmHelper.getAppLaunchIntent(targetPkg, c.user);
+                                    if (intent != null) {
+                                        c.restoreFlag = 0;
+                                        c.updater().put(
+                                                LauncherSettings.Favorites.INTENT,
+                                                intent.toUri(0)).commit();
+                                        cn = intent.getComponent();
                                     } else {
-                                        // The app is installed but the component is no
-                                        // longer available.
-                                        c.markDeleted("Invalid component removed: " + cn);
+                                        c.markDeleted("Unable to find a launch target");
                                         continue;
                                     }
                                 }
@@ -415,19 +419,20 @@
                                     // installed later.
                                     FileLog.d(TAG, "package not yet restored: " + targetPkg);
 
-                                    if (c.hasRestoreFlag(ShortcutInfo.FLAG_RESTORE_STARTED)) {
+                                    if (c.hasRestoreFlag(WorkspaceItemInfo.FLAG_RESTORE_STARTED)) {
                                         // Restore has started once.
                                     } else if (installingPkgs.containsKey(targetPkg)) {
                                         // App restore has started. Update the flag
-                                        c.restoreFlag |= ShortcutInfo.FLAG_RESTORE_STARTED;
-                                        c.updater().commit();
+                                        c.restoreFlag |= WorkspaceItemInfo.FLAG_RESTORE_STARTED;
+                                        c.updater().put(LauncherSettings.Favorites.RESTORED,
+                                                c.restoreFlag).commit();
                                     } else {
                                         c.markDeleted("Unrestored app removed: " + targetPkg);
                                         continue;
                                     }
                                 } else if (pmHelper.isAppOnSdcard(targetPkg, c.user)) {
                                     // Package is present but not available.
-                                    disabledState |= ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE;
+                                    disabledState |= WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE;
                                     // Add the icon on the workspace anyway.
                                     allowMissingTarget = true;
                                 } else if (!isSdCardReady) {
@@ -444,7 +449,7 @@
                                 }
                             }
 
-                            if ((c.restoreFlag & ShortcutInfo.FLAG_SUPPORTS_WEB_UI) != 0) {
+                            if ((c.restoreFlag & WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI) != 0) {
                                 validTarget = false;
                             }
 
@@ -454,8 +459,7 @@
                                 c.markRestored();
                             }
 
-                            boolean useLowResIcon = !c.isOnWorkspaceOrHotseat() &&
-                                    !verifier.isItemInPreview(c.getInt(rankIndex));
+                            boolean useLowResIcon = !c.isOnWorkspaceOrHotseat();
 
                             if (c.restoreFlag != 0) {
                                 // Already verified above that user is same as default user
@@ -469,27 +473,23 @@
 
                                 ShortcutKey key = ShortcutKey.fromIntent(intent, c.user);
                                 if (unlockedUsers.get(c.serialNumber)) {
-                                    ShortcutInfoCompat pinnedShortcut =
+                                    ShortcutInfo pinnedShortcut =
                                             shortcutKeyToPinnedShortcuts.get(key);
                                     if (pinnedShortcut == null) {
                                         // The shortcut is no longer valid.
                                         c.markDeleted("Pinned shortcut not found");
                                         continue;
                                     }
-                                    info = new ShortcutInfo(pinnedShortcut, context);
-                                    final ShortcutInfo finalInfo = info;
-                                    Provider<Bitmap> fallbackIconProvider = new Provider<Bitmap>() {
-                                        @Override
-                                        public Bitmap get() {
-                                            // If the pinned deep shortcut is no longer published,
-                                            // use the last saved icon instead of the default.
-                                            return c.loadIcon(finalInfo)
-                                                    ? finalInfo.iconBitmap : null;
-                                        }
-                                    };
+                                    info = new WorkspaceItemInfo(pinnedShortcut, context);
+                                    final WorkspaceItemInfo finalInfo = info;
+
                                     LauncherIcons li = LauncherIcons.obtain(context);
-                                    li.createShortcutIcon(pinnedShortcut,
-                                            true /* badged */, fallbackIconProvider).applyTo(info);
+                                    // If the pinned deep shortcut is no longer published,
+                                    // use the last saved icon instead of the default.
+                                    Supplier<ItemInfoWithIcon> fallbackIconProvider = () ->
+                                            c.loadIcon(finalInfo, li) ? finalInfo : null;
+                                    info.applyFrom(li.createShortcutIcon(pinnedShortcut,
+                                            true /* badged */, fallbackIconProvider));
                                     li.recycle();
                                     if (pmHelper.isAppSuspended(
                                             pinnedShortcut.getPackage(), info.user)) {
@@ -498,11 +498,11 @@
                                     intent = info.intent;
                                 } else {
                                     // Create a shortcut info in disabled mode for now.
-                                    info = c.loadSimpleShortcut();
+                                    info = c.loadSimpleWorkspaceItem();
                                     info.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
                                 }
                             } else { // item type == ITEM_TYPE_SHORTCUT
-                                info = c.loadSimpleShortcut();
+                                info = c.loadSimpleWorkspaceItem();
 
                                 // Shortcuts are only available on the primary profile
                                 if (!TextUtils.isEmpty(targetPkg)
@@ -538,7 +538,7 @@
                                 if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) {
                                     SessionInfo si = installingPkgs.get(targetPkg);
                                     if (si == null) {
-                                        info.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE;
+                                        info.status &= ~WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE;
                                     } else {
                                         info.setInstallProgress((int) (si.getProgress() * 100));
                                     }
@@ -546,7 +546,7 @@
 
                                 c.checkAndAddItem(info, mBgDataModel);
                             } else {
-                                throw new RuntimeException("Unexpected null ShortcutInfo");
+                                throw new RuntimeException("Unexpected null WorkspaceItemInfo");
                             }
                             break;
 
@@ -660,6 +660,11 @@
                                 appWidgetInfo.spanY = c.getInt(spanYIndex);
                                 appWidgetInfo.user = c.user;
 
+                                if (appWidgetInfo.spanX <= 0 || appWidgetInfo.spanY <= 0) {
+                                    c.markDeleted("Widget has invalid size: "
+                                            + appWidgetInfo.spanX + "x" + appWidgetInfo.spanY);
+                                    continue;
+                                }
                                 if (!c.isOnWorkspaceOrHotseat()) {
                                     c.markDeleted("Widget found where container != " +
                                             "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
@@ -710,11 +715,11 @@
             // Remove dead items
             if (c.commitDeleted()) {
                 // Remove any empty folder
-                ArrayList<Long> deletedFolderIds = (ArrayList<Long>) LauncherSettings.Settings
+                int[] deletedFolderIds = LauncherSettings.Settings
                         .call(contentResolver,
                                 LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS)
-                        .getSerializable(LauncherSettings.Settings.EXTRA_VALUE);
-                for (long folderId : deletedFolderIds) {
+                        .getIntArray(LauncherSettings.Settings.EXTRA_VALUE);
+                for (int folderId : deletedFolderIds) {
                     mBgDataModel.workspaceItems.remove(mBgDataModel.folders.get(folderId));
                     mBgDataModel.folders.remove(folderId);
                     mBgDataModel.itemsIdMap.remove(folderId);
@@ -737,24 +742,25 @@
                 }
             }
 
+            // Sort the folder items, update ranks, and make sure all preview items are high res.
             FolderIconPreviewVerifier verifier =
                     new FolderIconPreviewVerifier(mApp.getInvariantDeviceProfile());
-            // Sort the folder items and make sure all items in the preview are high resolution.
             for (FolderInfo folder : mBgDataModel.folders) {
                 Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR);
                 verifier.setFolderInfo(folder);
+                int size = folder.contents.size();
 
-                int numItemsInPreview = 0;
-                for (ShortcutInfo info : folder.contents) {
-                    if (info.usingLowResIcon
+                // Update ranks here to ensure there are no gaps caused by removed folder items.
+                // Ranks are the source of truth for folder items, so cellX and cellY can be ignored
+                // for now. Database will be updated once user manually modifies folder.
+                for (int rank = 0; rank < size; ++rank) {
+                    WorkspaceItemInfo info = folder.contents.get(rank);
+                    info.rank = rank;
+
+                    if (info.usingLowResIcon()
                             && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
                             && verifier.isItemInPreview(info.rank)) {
                         mIconCache.getTitleAndIcon(info, false);
-                        numItemsInPreview++;
-                    }
-
-                    if (numItemsInPreview >= MAX_NUM_ITEMS_IN_PREVIEW) {
-                        break;
                     }
                 }
             }
@@ -767,32 +773,16 @@
                         null,
                         new Handler(LauncherModel.getWorkerLooper()));
             }
-
-            // Remove any empty screens
-            ArrayList<Long> unusedScreens = new ArrayList<>(mBgDataModel.workspaceScreens);
-            for (ItemInfo item: mBgDataModel.itemsIdMap) {
-                long screenId = item.screenId;
-                if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
-                        unusedScreens.contains(screenId)) {
-                    unusedScreens.remove(screenId);
-                }
-            }
-
-            // If there are any empty screens remove them, and update.
-            if (unusedScreens.size() != 0) {
-                mBgDataModel.workspaceScreens.removeAll(unusedScreens);
-                LauncherModel.updateWorkspaceScreenOrder(context, mBgDataModel.workspaceScreens);
-            }
         }
     }
 
-    private void updateIconCache() {
+    private void setIgnorePackages(IconCacheUpdateHandler updateHandler) {
         // Ignore packages which have a promise icon.
         HashSet<String> packagesToIgnore = new HashSet<>();
         synchronized (mBgDataModel) {
             for (ItemInfo info : mBgDataModel.itemsIdMap) {
-                if (info instanceof ShortcutInfo) {
-                    ShortcutInfo si = (ShortcutInfo) info;
+                if (info instanceof WorkspaceItemInfo) {
+                    WorkspaceItemInfo si = (WorkspaceItemInfo) info;
                     if (si.isPromise() && si.getTargetComponent() != null) {
                         packagesToIgnore.add(si.getTargetComponent().getPackageName());
                     }
@@ -804,12 +794,12 @@
                 }
             }
         }
-        mIconCache.updateDbIcons(packagesToIgnore);
+        updateHandler.setPackagesToIgnore(Process.myUserHandle(), packagesToIgnore);
     }
 
-    private void loadAllApps() {
+    private List<LauncherActivityInfo> loadAllApps() {
         final List<UserHandle> profiles = mUserManager.getUserProfiles();
-
+        List<LauncherActivityInfo> allActivityList = new ArrayList<>();
         // Clear the list of apps
         mBgAllAppsList.clear();
         for (UserHandle user : profiles) {
@@ -818,7 +808,7 @@
             // Fail if we don't have any apps
             // TODO: Fix this. Only fail for the current user.
             if (apps == null || apps.isEmpty()) {
-                return;
+                return allActivityList;
             }
             boolean quietMode = mUserManager.isQuietModeEnabled(user);
             // Create the ApplicationInfos
@@ -827,6 +817,7 @@
                 // This builds the icon bitmaps.
                 mBgAllAppsList.add(new AppInfo(app, user, quietMode), app);
             }
+            allActivityList.addAll(apps);
         }
 
         if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS) {
@@ -839,6 +830,7 @@
         }
 
         mBgAllAppsList.added = new ArrayList<>();
+        return allActivityList;
     }
 
     private void loadDeepShortcuts() {
@@ -847,9 +839,9 @@
         if (mBgDataModel.hasShortcutHostPermission) {
             for (UserHandle user : mUserManager.getUserProfiles()) {
                 if (mUserManager.isUserUnlocked(user)) {
-                    List<ShortcutInfoCompat> shortcuts =
+                    List<ShortcutInfo> shortcuts =
                             mShortcutManager.queryForAllShortcuts(user);
-                    mBgDataModel.updateDeepShortcutMap(null, user, shortcuts);
+                    mBgDataModel.updateDeepShortcutCounts(null, user, shortcuts);
                 }
             }
         }
diff --git a/src/com/android/launcher3/model/ModelPreload.java b/src/com/android/launcher3/model/ModelPreload.java
index f186e95..b353810 100644
--- a/src/com/android/launcher3/model/ModelPreload.java
+++ b/src/com/android/launcher3/model/ModelPreload.java
@@ -16,7 +16,6 @@
 package com.android.launcher3.model;
 
 import android.content.Context;
-import android.support.annotation.WorkerThread;
 import android.util.Log;
 
 import com.android.launcher3.AllAppsList;
@@ -26,6 +25,8 @@
 
 import java.util.concurrent.Executor;
 
+import androidx.annotation.WorkerThread;
+
 /**
  * Utility class to preload LauncherModel
  */
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index eba7515..4ce2f4b 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -28,21 +28,26 @@
 import com.android.launcher3.FolderInfo;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetHost;
+import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherModel.Callbacks;
 import com.android.launcher3.LauncherProvider;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.LauncherSettings.Settings;
-import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.ContentWriter;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.LooperExecutor;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.concurrent.Executor;
+import java.util.function.Supplier;
 
 /**
  * Class for handling model updates.
@@ -60,6 +65,10 @@
     private final boolean mHasVerticalHotseat;
     private final boolean mVerifyChanges;
 
+    // Keep track of delete operations that occur when an Undo option is present; we may not commit.
+    private final List<Runnable> mDeleteRunnables = new ArrayList<>();
+    private boolean mPreparingToUndo;
+
     public ModelWriter(Context context, LauncherModel model, BgDataModel dataModel,
             boolean hasVerticalHotseat, boolean verifyChanges) {
         mContext = context;
@@ -72,7 +81,7 @@
     }
 
     private void updateItemInfoProps(
-            ItemInfo item, long container, long screenId, int cellX, int cellY) {
+            ItemInfo item, int container, int screenId, int cellX, int cellY) {
         item.container = container;
         item.cellX = cellX;
         item.cellY = cellY;
@@ -91,7 +100,7 @@
      * <container, screen, cellX, cellY>
      */
     public void addOrMoveItemInDatabase(ItemInfo item,
-            long container, long screenId, int cellX, int cellY) {
+            int container, int screenId, int cellX, int cellY) {
         if (item.container == ItemInfo.NO_ID) {
             // From all apps
             addItemToDatabase(item, container, screenId, cellX, cellY);
@@ -101,23 +110,22 @@
         }
     }
 
-    private void checkItemInfoLocked(long itemId, ItemInfo item, StackTraceElement[] stackTrace) {
+    private void checkItemInfoLocked(int itemId, ItemInfo item, StackTraceElement[] stackTrace) {
         ItemInfo modelItem = mBgDataModel.itemsIdMap.get(itemId);
         if (modelItem != null && item != modelItem) {
             // check all the data is consistent
-            if (modelItem instanceof ShortcutInfo && item instanceof ShortcutInfo) {
-                ShortcutInfo modelShortcut = (ShortcutInfo) modelItem;
-                ShortcutInfo shortcut = (ShortcutInfo) item;
-                if (modelShortcut.title.toString().equals(shortcut.title.toString()) &&
-                        modelShortcut.intent.filterEquals(shortcut.intent) &&
-                        modelShortcut.id == shortcut.id &&
-                        modelShortcut.itemType == shortcut.itemType &&
-                        modelShortcut.container == shortcut.container &&
-                        modelShortcut.screenId == shortcut.screenId &&
-                        modelShortcut.cellX == shortcut.cellX &&
-                        modelShortcut.cellY == shortcut.cellY &&
-                        modelShortcut.spanX == shortcut.spanX &&
-                        modelShortcut.spanY == shortcut.spanY) {
+            if (!Utilities.IS_DEBUG_DEVICE && !FeatureFlags.IS_DOGFOOD_BUILD &&
+                    modelItem instanceof WorkspaceItemInfo && item instanceof WorkspaceItemInfo) {
+                if (modelItem.title.toString().equals(item.title.toString()) &&
+                        modelItem.getIntent().filterEquals(item.getIntent()) &&
+                        modelItem.id == item.id &&
+                        modelItem.itemType == item.itemType &&
+                        modelItem.container == item.container &&
+                        modelItem.screenId == item.screenId &&
+                        modelItem.cellX == item.cellX &&
+                        modelItem.cellY == item.cellY &&
+                        modelItem.spanX == item.spanX &&
+                        modelItem.spanY == item.spanY) {
                     // For all intents and purposes, this is the same object
                     return;
                 }
@@ -142,24 +150,22 @@
      * Move an item in the DB to a new <container, screen, cellX, cellY>
      */
     public void moveItemInDatabase(final ItemInfo item,
-            long container, long screenId, int cellX, int cellY) {
+            int container, int screenId, int cellX, int cellY) {
         updateItemInfoProps(item, container, screenId, cellX, cellY);
-
-        final ContentWriter writer = new ContentWriter(mContext)
-                .put(Favorites.CONTAINER, item.container)
-                .put(Favorites.CELLX, item.cellX)
-                .put(Favorites.CELLY, item.cellY)
-                .put(Favorites.RANK, item.rank)
-                .put(Favorites.SCREEN, item.screenId);
-
-        mWorkerExecutor.execute(new UpdateItemRunnable(item, writer));
+        enqueueDeleteRunnable(new UpdateItemRunnable(item, () ->
+                new ContentWriter(mContext)
+                        .put(Favorites.CONTAINER, item.container)
+                        .put(Favorites.CELLX, item.cellX)
+                        .put(Favorites.CELLY, item.cellY)
+                        .put(Favorites.RANK, item.rank)
+                        .put(Favorites.SCREEN, item.screenId)));
     }
 
     /**
      * Move items in the DB to a new <container, screen, cellX, cellY>. We assume that the
      * cellX, cellY have already been updated on the ItemInfos.
      */
-    public void moveItemsInDatabase(final ArrayList<ItemInfo> items, long container, int screen) {
+    public void moveItemsInDatabase(final ArrayList<ItemInfo> items, int container, int screen) {
         ArrayList<ContentValues> contentValues = new ArrayList<>();
         int count = items.size();
 
@@ -176,37 +182,38 @@
 
             contentValues.add(values);
         }
-        mWorkerExecutor.execute(new UpdateItemsRunnable(items, contentValues));
+        enqueueDeleteRunnable(new UpdateItemsRunnable(items, contentValues));
     }
 
     /**
      * Move and/or resize item in the DB to a new <container, screen, cellX, cellY, spanX, spanY>
      */
     public void modifyItemInDatabase(final ItemInfo item,
-            long container, long screenId, int cellX, int cellY, int spanX, int spanY) {
+            int container, int screenId, int cellX, int cellY, int spanX, int spanY) {
         updateItemInfoProps(item, container, screenId, cellX, cellY);
         item.spanX = spanX;
         item.spanY = spanY;
 
-        final ContentWriter writer = new ContentWriter(mContext)
-                .put(Favorites.CONTAINER, item.container)
-                .put(Favorites.CELLX, item.cellX)
-                .put(Favorites.CELLY, item.cellY)
-                .put(Favorites.RANK, item.rank)
-                .put(Favorites.SPANX, item.spanX)
-                .put(Favorites.SPANY, item.spanY)
-                .put(Favorites.SCREEN, item.screenId);
-
-        mWorkerExecutor.execute(new UpdateItemRunnable(item, writer));
+        mWorkerExecutor.execute(new UpdateItemRunnable(item, () ->
+                new ContentWriter(mContext)
+                        .put(Favorites.CONTAINER, item.container)
+                        .put(Favorites.CELLX, item.cellX)
+                        .put(Favorites.CELLY, item.cellY)
+                        .put(Favorites.RANK, item.rank)
+                        .put(Favorites.SPANX, item.spanX)
+                        .put(Favorites.SPANY, item.spanY)
+                        .put(Favorites.SCREEN, item.screenId)));
     }
 
     /**
      * Update an item to the database in a specified container.
      */
     public void updateItemInDatabase(ItemInfo item) {
-        ContentWriter writer = new ContentWriter(mContext);
-        item.onAddToDatabase(writer);
-        mWorkerExecutor.execute(new UpdateItemRunnable(item, writer));
+        mWorkerExecutor.execute(new UpdateItemRunnable(item, () -> {
+            ContentWriter writer = new ContentWriter(mContext);
+            item.onAddToDatabase(writer);
+            return writer;
+        }));
     }
 
     /**
@@ -214,20 +221,21 @@
      * cellY fields of the item. Also assigns an ID to the item.
      */
     public void addItemToDatabase(final ItemInfo item,
-            long container, long screenId, int cellX, int cellY) {
+            int container, int screenId, int cellX, int cellY) {
         updateItemInfoProps(item, container, screenId, cellX, cellY);
 
-        final ContentWriter writer = new ContentWriter(mContext);
         final ContentResolver cr = mContext.getContentResolver();
-        item.onAddToDatabase(writer);
-
-        item.id = Settings.call(cr, Settings.METHOD_NEW_ITEM_ID).getLong(Settings.EXTRA_VALUE);
-        writer.put(Favorites._ID, item.id);
+        item.id = Settings.call(cr, Settings.METHOD_NEW_ITEM_ID).getInt(Settings.EXTRA_VALUE);
 
         ModelVerifier verifier = new ModelVerifier();
-
         final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
         mWorkerExecutor.execute(() -> {
+            // Write the item on background thread, as some properties might have been updated in
+            // the background.
+            final ContentWriter writer = new ContentWriter(mContext);
+            item.onAddToDatabase(writer);
+            writer.put(Favorites._ID, item.id);
+
             cr.insert(Favorites.CONTENT_URI, writer.getValues(mContext));
 
             synchronized (mBgDataModel) {
@@ -258,7 +266,7 @@
     public void deleteItemsFromDatabase(final Iterable<? extends ItemInfo> items) {
         ModelVerifier verifier = new ModelVerifier();
 
-        mWorkerExecutor.execute(() -> {
+        enqueueDeleteRunnable(() -> {
             for (ItemInfo item : items) {
                 final Uri uri = Favorites.getContentUri(item.id);
                 mContext.getContentResolver().delete(uri, null, null);
@@ -275,7 +283,7 @@
     public void deleteFolderAndContentsFromDatabase(final FolderInfo info) {
         ModelVerifier verifier = new ModelVerifier();
 
-        mWorkerExecutor.execute(() -> {
+        enqueueDeleteRunnable(() -> {
             ContentResolver cr = mContext.getContentResolver();
             cr.delete(LauncherSettings.Favorites.CONTENT_URI,
                     LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
@@ -288,12 +296,69 @@
         });
     }
 
+    /**
+     * Deletes the widget info and the widget id.
+     */
+    public void deleteWidgetInfo(final LauncherAppWidgetInfo info, LauncherAppWidgetHost host) {
+        if (host != null && !info.isCustomWidget() && info.isWidgetIdAllocated()) {
+            // Deleting an app widget ID is a void call but writes to disk before returning
+            // to the caller...
+            enqueueDeleteRunnable(() -> host.deleteAppWidgetId(info.appWidgetId));
+        }
+        deleteItemFromDatabase(info);
+    }
+
+    /**
+     * Delete operations tracked using {@link #enqueueDeleteRunnable} will only be called
+     * if {@link #commitDelete} is called. Note that one of {@link #commitDelete()} or
+     * {@link #abortDelete} MUST be called after this method, or else all delete
+     * operations will remain uncommitted indefinitely.
+     */
+    public void prepareToUndoDelete() {
+        if (!mPreparingToUndo) {
+            if (!mDeleteRunnables.isEmpty() && FeatureFlags.IS_DOGFOOD_BUILD) {
+                throw new IllegalStateException("There are still uncommitted delete operations!");
+            }
+            mDeleteRunnables.clear();
+            mPreparingToUndo = true;
+        }
+    }
+
+    /**
+     * If {@link #prepareToUndoDelete} has been called, we store the Runnable to be run when
+     * {@link #commitDelete()} is called (or abandoned if {@link #abortDelete} is called).
+     * Otherwise, we run the Runnable immediately.
+     */
+    private void enqueueDeleteRunnable(Runnable r) {
+        if (mPreparingToUndo) {
+            mDeleteRunnables.add(r);
+        } else {
+            mWorkerExecutor.execute(r);
+        }
+    }
+
+    public void commitDelete() {
+        mPreparingToUndo = false;
+        for (Runnable runnable : mDeleteRunnables) {
+            mWorkerExecutor.execute(runnable);
+        }
+        mDeleteRunnables.clear();
+    }
+
+    public void abortDelete(int pageToBindFirst) {
+        mPreparingToUndo = false;
+        mDeleteRunnables.clear();
+        // We do a full reload here instead of just a rebind because Folders change their internal
+        // state when dragging an item out, which clobbers the rebind unless we load from the DB.
+        mModel.forceReload(pageToBindFirst);
+    }
+
     private class UpdateItemRunnable extends UpdateItemBaseRunnable {
         private final ItemInfo mItem;
-        private final ContentWriter mWriter;
-        private final long mItemId;
+        private final Supplier<ContentWriter> mWriter;
+        private final int mItemId;
 
-        UpdateItemRunnable(ItemInfo item, ContentWriter writer) {
+        UpdateItemRunnable(ItemInfo item, Supplier<ContentWriter> writer) {
             mItem = item;
             mWriter = writer;
             mItemId = item.id;
@@ -302,7 +367,8 @@
         @Override
         public void run() {
             Uri uri = Favorites.getContentUri(mItemId);
-            mContext.getContentResolver().update(uri, mWriter.getValues(mContext), null, null);
+            mContext.getContentResolver().update(uri, mWriter.get().getValues(mContext),
+                    null, null);
             updateItemArrays(mItem, mItemId);
         }
     }
@@ -322,7 +388,7 @@
             int count = mItems.size();
             for (int i = 0; i < count; i++) {
                 ItemInfo item = mItems.get(i);
-                final long itemId = item.id;
+                final int itemId = item.id;
                 final Uri uri = Favorites.getContentUri(itemId);
                 ContentValues values = mValues.get(i);
 
@@ -345,7 +411,7 @@
             mStackTrace = new Throwable().getStackTrace();
         }
 
-        protected void updateItemArrays(ItemInfo item, long itemId) {
+        protected void updateItemArrays(ItemInfo item, int itemId) {
             // Lock on mBgLock *after* the db operation
             synchronized (mBgDataModel) {
                 checkItemInfoLocked(itemId, item, mStackTrace);
diff --git a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
index 32dfe25..5f6d128 100644
--- a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
+++ b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
@@ -28,7 +28,7 @@
 import com.android.launcher3.LauncherModel.CallbackTask;
 import com.android.launcher3.LauncherModel.Callbacks;
 import com.android.launcher3.PromiseAppInfo;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.compat.PackageInstallerCompat;
 import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
 import com.android.launcher3.util.InstantAppResolver;
@@ -106,15 +106,15 @@
         synchronized (dataModel) {
             final HashSet<ItemInfo> updates = new HashSet<>();
             for (ItemInfo info : dataModel.itemsIdMap) {
-                if (info instanceof ShortcutInfo) {
-                    ShortcutInfo si = (ShortcutInfo) info;
+                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 == PackageInstallerCompat.STATUS_FAILED) {
                             // Mark this info as broken.
-                            si.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE;
+                            si.status &= ~WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE;
                         }
                         updates.add(si);
                     }
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 977dcd7..c37ed99 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -18,14 +18,15 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ShortcutInfo;
 import android.os.Process;
 import android.os.UserHandle;
-import android.util.ArrayMap;
 import android.util.Log;
 
 import com.android.launcher3.AllAppsList;
 import com.android.launcher3.AppInfo;
-import com.android.launcher3.IconCache;
+import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.InstallShortcutReceiver;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
@@ -34,19 +35,17 @@
 import com.android.launcher3.LauncherModel.Callbacks;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.SessionCommitReceiver;
-import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.graphics.BitmapInfo;
-import com.android.launcher3.graphics.LauncherIcons;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.util.FlagOp;
+import com.android.launcher3.util.IntSparseArrayMap;
 import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.LongArrayMap;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.PackageUserKey;
 
@@ -109,7 +108,7 @@
                         SessionCommitReceiver.queueAppIconAddition(context, packages[i], mUser);
                     }
                 }
-                flagOp = FlagOp.removeFlag(ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE);
+                flagOp = FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
                 break;
             }
             case OP_UPDATE:
@@ -120,7 +119,7 @@
                     app.getWidgetCache().removePackage(packages[i], mUser);
                 }
                 // Since package was just updated, the target must be available now.
-                flagOp = FlagOp.removeFlag(ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE);
+                flagOp = FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
                 break;
             case OP_REMOVE: {
                 for (int i = 0; i < N; i++) {
@@ -134,20 +133,20 @@
                     appsList.removePackage(packages[i], mUser);
                     app.getWidgetCache().removePackage(packages[i], mUser);
                 }
-                flagOp = FlagOp.addFlag(ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE);
+                flagOp = FlagOp.addFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
                 break;
             case OP_SUSPEND:
             case OP_UNSUSPEND:
                 flagOp = mOp == OP_SUSPEND ?
-                        FlagOp.addFlag(ShortcutInfo.FLAG_DISABLED_SUSPENDED) :
-                        FlagOp.removeFlag(ShortcutInfo.FLAG_DISABLED_SUSPENDED);
+                        FlagOp.addFlag(WorkspaceItemInfo.FLAG_DISABLED_SUSPENDED) :
+                        FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_SUSPENDED);
                 if (DEBUG) Log.d(TAG, "mAllAppsList.(un)suspend " + N);
                 appsList.updateDisabledFlags(matcher, flagOp);
                 break;
             case OP_USER_AVAILABILITY_CHANGE:
                 flagOp = UserManagerCompat.getInstance(context).isQuietModeEnabled(mUser)
-                        ? FlagOp.addFlag(ShortcutInfo.FLAG_DISABLED_QUIET_USER)
-                        : FlagOp.removeFlag(ShortcutInfo.FLAG_DISABLED_QUIET_USER);
+                        ? 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);
@@ -159,31 +158,32 @@
         appsList.added.clear();
         addedOrModified.addAll(appsList.modified);
         appsList.modified.clear();
+        if (!addedOrModified.isEmpty()) {
+            scheduleCallbackTask((callbacks) -> callbacks.bindAppsAddedOrUpdated(addedOrModified));
+        }
 
         final ArrayList<AppInfo> removedApps = new ArrayList<>(appsList.removed);
         appsList.removed.clear();
-
-        final ArrayMap<ComponentName, AppInfo> addedOrUpdatedApps = new ArrayMap<>();
-        if (!addedOrModified.isEmpty()) {
-            scheduleCallbackTask((callbacks) -> callbacks.bindAppsAddedOrUpdated(addedOrModified));
-            for (AppInfo ai : addedOrModified) {
-                addedOrUpdatedApps.put(ai.componentName, ai);
+        final HashSet<ComponentName> removedComponents = new HashSet<>();
+        if (mOp == OP_UPDATE) {
+            for (AppInfo ai : removedApps) {
+                removedComponents.add(ai.componentName);
             }
         }
 
-        final LongArrayMap<Boolean> removedShortcuts = new LongArrayMap<>();
+        final IntSparseArrayMap<Boolean> removedShortcuts = new IntSparseArrayMap<>();
 
         // Update shortcut infos
         if (mOp == OP_ADD || flagOp != FlagOp.NO_OP) {
-            final ArrayList<ShortcutInfo> updatedShortcuts = new ArrayList<>();
+            final ArrayList<WorkspaceItemInfo> updatedWorkspaceItems = new ArrayList<>();
             final ArrayList<LauncherAppWidgetInfo> widgets = new ArrayList<>();
 
             // 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 ShortcutInfo && mUser.equals(info.user)) {
-                        ShortcutInfo si = (ShortcutInfo) info;
+                    if (info instanceof WorkspaceItemInfo && mUser.equals(info.user)) {
+                        WorkspaceItemInfo si = (WorkspaceItemInfo) info;
                         boolean infoUpdated = false;
                         boolean shortcutUpdated = false;
 
@@ -194,16 +194,16 @@
                             BitmapInfo iconInfo = li.createIconBitmap(si.iconResource);
                             li.recycle();
                             if (iconInfo != null) {
-                                iconInfo.applyTo(si);
+                                si.applyFrom(iconInfo);
                                 infoUpdated = true;
                             }
                         }
 
                         ComponentName cn = si.getTargetComponent();
                         if (cn != null && matcher.matches(si, cn)) {
-                            AppInfo appInfo = addedOrUpdatedApps.get(cn);
+                            String packageName = cn.getPackageName();
 
-                            if (si.hasStatusFlag(ShortcutInfo.FLAG_SUPPORTS_WEB_UI)) {
+                            if (si.hasStatusFlag(WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI)) {
                                 removedShortcuts.put(si.id, false);
                                 if (mOp == OP_REMOVE) {
                                     continue;
@@ -213,7 +213,7 @@
                             if (si.isPromise() && isNewApkAvailable) {
                                 boolean isTargetValid = true;
                                 if (si.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
-                                    List<ShortcutInfoCompat> shortcut = DeepShortcutManager
+                                    List<ShortcutInfo> shortcut = DeepShortcutManager
                                             .getInstance(context).queryForPinnedShortcuts(
                                                     cn.getPackageName(),
                                                     Arrays.asList(si.getDeepShortcutId()), mUser);
@@ -227,26 +227,13 @@
                                     isTargetValid = LauncherAppsCompat.getInstance(context)
                                             .isActivityEnabledForProfile(cn, mUser);
                                 }
-
-                                if (si.hasStatusFlag(ShortcutInfo.FLAG_AUTOINSTALL_ICON)) {
-                                    // Auto install icon
-                                    if (!isTargetValid) {
-                                        // Try to find the best match activity.
-                                        Intent intent = new PackageManagerHelper(context)
-                                                .getAppLaunchIntent(cn.getPackageName(), mUser);
-                                        if (intent != null) {
-                                            cn = intent.getComponent();
-                                            appInfo = addedOrUpdatedApps.get(cn);
-                                        }
-
-                                        if (intent != null && appInfo != null) {
-                                            si.intent = intent;
-                                            si.status = ShortcutInfo.DEFAULT;
-                                            infoUpdated = true;
-                                        } else if (si.hasPromiseIconUi()) {
-                                            removedShortcuts.put(si.id, true);
-                                            continue;
-                                        }
+                                if (si.hasStatusFlag(WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON)
+                                        && !isTargetValid) {
+                                    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);
@@ -254,14 +241,18 @@
                                             + si.intent);
                                     continue;
                                 } else {
-                                    si.status = ShortcutInfo.DEFAULT;
+                                    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) {
-                                iconCache.getTitleAndIcon(si, si.usingLowResIcon);
+                                iconCache.getTitleAndIcon(si, si.usingLowResIcon());
                                 infoUpdated = true;
                             }
 
@@ -273,7 +264,7 @@
                         }
 
                         if (infoUpdated || shortcutUpdated) {
-                            updatedShortcuts.add(si);
+                            updatedWorkspaceItems.add(si);
                         }
                         if (infoUpdated) {
                             getModelWriter().updateItemInDatabase(si);
@@ -299,7 +290,7 @@
                 }
             }
 
-            bindUpdatedShortcuts(updatedShortcuts, mUser);
+            bindUpdatedWorkspaceItems(updatedWorkspaceItems);
             if (!removedShortcuts.isEmpty()) {
                 deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedShortcuts, false));
             }
@@ -315,7 +306,6 @@
         }
 
         final HashSet<String> removedPackages = new HashSet<>();
-        final HashSet<ComponentName> removedComponents = new HashSet<>();
         if (mOp == OP_REMOVE) {
             // Mark all packages in the broadcast to be removed
             Collections.addAll(removedPackages, packages);
@@ -330,11 +320,6 @@
                     removedPackages.add(packages[i]);
                 }
             }
-
-            // Update removedComponents as some components can get removed during package update
-            for (AppInfo info : removedApps) {
-                removedComponents.add(info.componentName);
-            }
         }
 
         if (!removedPackages.isEmpty() || !removedComponents.isEmpty()) {
@@ -366,4 +351,20 @@
             bindUpdatedWidgets(dataModel);
         }
     }
+
+    /**
+     * Updates {@param si}'s intent to point to a new ComponentName.
+     * @return Whether the shortcut intent was changed.
+     */
+    private boolean updateWorkspaceItemIntent(Context context,
+            WorkspaceItemInfo si, String packageName) {
+        // Try to find the best match activity.
+        Intent intent = new PackageManagerHelper(context).getAppLaunchIntent(packageName, mUser);
+        if (intent != null) {
+            si.intent = intent;
+            si.status = WorkspaceItemInfo.DEFAULT;
+            return true;
+        }
+        return false;
+    }
 }
diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.java b/src/com/android/launcher3/model/ShortcutsChangedTask.java
index 59f3d1c..8528228 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.java
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java
@@ -16,20 +16,19 @@
 package com.android.launcher3.model;
 
 import android.content.Context;
+import android.content.pm.ShortcutInfo;
 import android.os.UserHandle;
 
 import com.android.launcher3.AllAppsList;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.graphics.LauncherIcons;
+import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.MultiHashMap;
-import com.android.launcher3.util.Provider;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -41,11 +40,11 @@
 public class ShortcutsChangedTask extends BaseModelUpdateTask {
 
     private final String mPackageName;
-    private final List<ShortcutInfoCompat> mShortcuts;
+    private final List<ShortcutInfo> mShortcuts;
     private final UserHandle mUser;
     private final boolean mUpdateIdMap;
 
-    public ShortcutsChangedTask(String packageName, List<ShortcutInfoCompat> shortcuts,
+    public ShortcutsChangedTask(String packageName, List<ShortcutInfo> shortcuts,
             UserHandle user, boolean updateIdMap) {
         mPackageName = packageName;
         mShortcuts = shortcuts;
@@ -57,16 +56,15 @@
     public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
         final Context context = app.getContext();
         DeepShortcutManager deepShortcutManager = DeepShortcutManager.getInstance(context);
-        deepShortcutManager.onShortcutsChanged(mShortcuts);
 
-        // Find ShortcutInfo's that have changed on the workspace.
+        // Find WorkspaceItemInfo's that have changed on the workspace.
         HashSet<ShortcutKey> removedKeys = new HashSet<>();
-        MultiHashMap<ShortcutKey, ShortcutInfo> keyToShortcutInfo = new MultiHashMap<>();
+        MultiHashMap<ShortcutKey, WorkspaceItemInfo> keyToShortcutInfo = new MultiHashMap<>();
         HashSet<String> allIds = new HashSet<>();
 
         for (ItemInfo itemInfo : dataModel.itemsIdMap) {
             if (itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
-                ShortcutInfo si = (ShortcutInfo) itemInfo;
+                WorkspaceItemInfo si = (WorkspaceItemInfo) itemInfo;
                 if (si.getIntent().getPackage().equals(mPackageName) && si.user.equals(mUser)) {
                     keyToShortcutInfo.addToList(ShortcutKey.fromItemInfo(si), si);
                     allIds.add(si.getDeepShortcutId());
@@ -74,14 +72,14 @@
             }
         }
 
-        final ArrayList<ShortcutInfo> updatedShortcutInfos = new ArrayList<>();
+        final ArrayList<WorkspaceItemInfo> updatedWorkspaceItemInfos = new ArrayList<>();
         if (!keyToShortcutInfo.isEmpty()) {
             // Update the workspace to reflect the changes to updated shortcuts residing on it.
-            List<ShortcutInfoCompat> shortcuts = deepShortcutManager.queryForFullDetails(
+            List<ShortcutInfo> shortcuts = deepShortcutManager.queryForFullDetails(
                     mPackageName, new ArrayList<>(allIds), mUser);
-            for (ShortcutInfoCompat fullDetails : shortcuts) {
+            for (ShortcutInfo fullDetails : shortcuts) {
                 ShortcutKey key = ShortcutKey.fromInfo(fullDetails);
-                List<ShortcutInfo> shortcutInfos = keyToShortcutInfo.remove(key);
+                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.
@@ -91,15 +89,15 @@
                     removedKeys.add(key);
                     continue;
                 }
-                for (final ShortcutInfo shortcutInfo : shortcutInfos) {
-                    shortcutInfo.updateFromDeepShortcutInfo(fullDetails, context);
+                for (final WorkspaceItemInfo workspaceItemInfo : workspaceItemInfos) {
+                    workspaceItemInfo.updateFromDeepShortcutInfo(fullDetails, context);
                     // If the shortcut is pinned but no longer has an icon in the system,
                     // keep the current icon instead of reverting to the default icon.
                     LauncherIcons li = LauncherIcons.obtain(context);
-                    li.createShortcutIcon(fullDetails, true, Provider.of(shortcutInfo.iconBitmap))
-                            .applyTo(shortcutInfo);
+                    workspaceItemInfo.applyFrom(li.createShortcutIcon(fullDetails, true,
+                            () -> workspaceItemInfo));
                     li.recycle();
-                    updatedShortcutInfos.add(shortcutInfo);
+                    updatedWorkspaceItemInfos.add(workspaceItemInfo);
                 }
             }
         }
@@ -109,14 +107,14 @@
         // means they were cleared, so we remove and unpin them now.
         removedKeys.addAll(keyToShortcutInfo.keySet());
 
-        bindUpdatedShortcuts(updatedShortcutInfos, mUser);
+        bindUpdatedWorkspaceItems(updatedWorkspaceItemInfos);
         if (!keyToShortcutInfo.isEmpty()) {
             deleteAndBindComponentsRemoved(ItemInfoMatcher.ofShortcutKeys(removedKeys));
         }
 
         if (mUpdateIdMap) {
             // Update the deep shortcut map if the list of ids has changed for an activity.
-            dataModel.updateDeepShortcutMap(mPackageName, mUser, mShortcuts);
+            dataModel.updateDeepShortcutCounts(mPackageName, mUser, mShortcuts);
             bindDeepShortcuts(dataModel);
         }
     }
diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java
index 9521a9e..2cb256e 100644
--- a/src/com/android/launcher3/model/UserLockStateChangedTask.java
+++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java
@@ -18,21 +18,20 @@
 import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
 
 import android.content.Context;
+import android.content.pm.ShortcutInfo;
 import android.os.UserHandle;
 
 import com.android.launcher3.AllAppsList;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.graphics.LauncherIcons;
+import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.Provider;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -57,12 +56,12 @@
         boolean isUserUnlocked = UserManagerCompat.getInstance(context).isUserUnlocked(mUser);
         DeepShortcutManager deepShortcutManager = DeepShortcutManager.getInstance(context);
 
-        HashMap<ShortcutKey, ShortcutInfoCompat> pinnedShortcuts = new HashMap<>();
+        HashMap<ShortcutKey, ShortcutInfo> pinnedShortcuts = new HashMap<>();
         if (isUserUnlocked) {
-            List<ShortcutInfoCompat> shortcuts =
+            List<ShortcutInfo> shortcuts =
                     deepShortcutManager.queryForPinnedShortcuts(null, mUser);
             if (deepShortcutManager.wasLastCallSuccess()) {
-                for (ShortcutInfoCompat shortcut : shortcuts) {
+                for (ShortcutInfo shortcut : shortcuts) {
                     pinnedShortcuts.put(ShortcutKey.fromInfo(shortcut), shortcut);
                 }
             } else {
@@ -74,16 +73,16 @@
         }
 
         // Update the workspace to reflect the changes to updated shortcuts residing on it.
-        ArrayList<ShortcutInfo> updatedShortcutInfos = new ArrayList<>();
+        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)) {
-                ShortcutInfo si = (ShortcutInfo) itemInfo;
+                WorkspaceItemInfo si = (WorkspaceItemInfo) itemInfo;
                 if (isUserUnlocked) {
                     ShortcutKey key = ShortcutKey.fromItemInfo(si);
-                    ShortcutInfoCompat shortcut = pinnedShortcuts.get(key);
+                    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) {
@@ -95,15 +94,15 @@
                     // If the shortcut is pinned but no longer has an icon in the system,
                     // keep the current icon instead of reverting to the default icon.
                     LauncherIcons li = LauncherIcons.obtain(context);
-                    li.createShortcutIcon(shortcut, true, Provider.of(si.iconBitmap)).applyTo(si);
+                    si.applyFrom(li.createShortcutIcon(shortcut, true, () -> si));
                     li.recycle();
                 } else {
                     si.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
                 }
-                updatedShortcutInfos.add(si);
+                updatedWorkspaceItemInfos.add(si);
             }
         }
-        bindUpdatedShortcuts(updatedShortcutInfos, mUser);
+        bindUpdatedWorkspaceItems(updatedWorkspaceItemInfos);
         if (!removedKeys.isEmpty()) {
             deleteAndBindComponentsRemoved(ItemInfoMatcher.ofShortcutKeys(removedKeys));
         }
@@ -117,7 +116,7 @@
         }
 
         if (isUserUnlocked) {
-            dataModel.updateDeepShortcutMap(
+            dataModel.updateDeepShortcutCounts(
                     null, mUser, deepShortcutManager.queryForAllShortcuts(mUser));
         }
         bindDeepShortcuts(dataModel);
diff --git a/src/com/android/launcher3/model/WidgetItem.java b/src/com/android/launcher3/model/WidgetItem.java
index 1e96dec..e38529b 100644
--- a/src/com/android/launcher3/model/WidgetItem.java
+++ b/src/com/android/launcher3/model/WidgetItem.java
@@ -9,6 +9,7 @@
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.ShortcutConfigActivityInfo;
+import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.util.ComponentKey;
 
 import java.text.Collator;
@@ -29,11 +30,11 @@
     public final String label;
     public final int spanX, spanY;
 
-    public WidgetItem(LauncherAppWidgetProviderInfo info, PackageManager pm,
-            InvariantDeviceProfile idp) {
+    public WidgetItem(LauncherAppWidgetProviderInfo info,
+            InvariantDeviceProfile idp, IconCache iconCache) {
         super(info.provider, info.getProfile());
 
-        label = Utilities.trim(info.getLabel(pm));
+        label = iconCache.getTitleNoCache(info);
         widgetInfo = info;
         activityInfo = null;
 
@@ -41,9 +42,10 @@
         spanY = Math.min(info.spanY, idp.numRows);
     }
 
-    public WidgetItem(ShortcutConfigActivityInfo info) {
+    public WidgetItem(ShortcutConfigActivityInfo info, IconCache iconCache, PackageManager pm) {
         super(info.getComponent(), info.getUser());
-        label = Utilities.trim(info.getLabel());
+        label = info.isPersistable() ? iconCache.getTitleNoCache(info) :
+                Utilities.trim(info.getLabel(pm));
         widgetInfo = null;
         activityInfo = info;
         spanX = spanY = 1;
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
deleted file mode 100644
index 9f8f263..0000000
--- a/src/com/android/launcher3/model/WidgetsModel.java
+++ /dev/null
@@ -1,206 +0,0 @@
-
-package com.android.launcher3.model;
-
-import static android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_HIDE_FROM_PICKER;
-
-import android.appwidget.AppWidgetProviderInfo;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.os.Process;
-import android.os.UserHandle;
-import android.support.annotation.Nullable;
-import android.util.Log;
-
-import com.android.launcher3.AppFilter;
-import com.android.launcher3.IconCache;
-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.compat.AppWidgetManagerCompat;
-import com.android.launcher3.compat.LauncherAppsCompat;
-import com.android.launcher3.compat.ShortcutConfigActivityInfo;
-import com.android.launcher3.config.FeatureFlags;
-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 java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-
-/**
- * Widgets data model that is used by the adapters of the widget views and controllers.
- *
- * <p> The widgets and shortcuts are organized using package name as its index.
- */
-public class WidgetsModel {
-
-    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 AppFilter mAppFilter;
-
-    /**
-     * 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}
-     *
-     * @see com.android.launcher3.widget.WidgetsListAdapter#setWidgets(ArrayList)
-     */
-    public synchronized ArrayList<WidgetListRowEntry> getWidgetsList(Context context) {
-        ArrayList<WidgetListRowEntry> 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 = indexer.computeSectionName(row.pkgItem.title);
-            Collections.sort(row.widgets, widgetComparator);
-            result.add(row);
-        }
-        return result;
-    }
-
-    /**
-     * @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise
-     *                    only widgets and shortcuts associated with the package/user are.
-     */
-    public void update(LauncherAppState app, @Nullable PackageUserKey packageUser) {
-        Preconditions.assertWorkerThread();
-
-        Context context = app.getContext();
-        final ArrayList<WidgetItem> widgetsAndShortcuts = new ArrayList<>();
-        try {
-            PackageManager pm = context.getPackageManager();
-            InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
-
-            // Widgets
-            AppWidgetManagerCompat widgetManager = AppWidgetManagerCompat.getInstance(context);
-            for (AppWidgetProviderInfo widgetInfo : widgetManager.getAllProviders(packageUser)) {
-                widgetsAndShortcuts.add(new WidgetItem(LauncherAppWidgetProviderInfo
-                        .fromProviderInfo(context, widgetInfo), pm, idp));
-            }
-
-            // Shortcuts
-            for (ShortcutConfigActivityInfo info : LauncherAppsCompat.getInstance(context)
-                    .getCustomShortcutActivityList(packageUser)) {
-                widgetsAndShortcuts.add(new WidgetItem(info));
-            }
-            setWidgetsAndShortcuts(widgetsAndShortcuts, app, packageUser);
-        } catch (Exception e) {
-            if (!FeatureFlags.IS_DOGFOOD_BUILD && Utilities.isBinderSizeError(e)) {
-                // the returned value may be incomplete and will not be refreshed until the next
-                // time Launcher starts.
-                // TODO: after figuring out a repro step, introduce a dirty bit to check when
-                // onResume is called to refresh the widget provider list.
-            } else {
-                throw e;
-            }
-        }
-
-        app.getWidgetCache().removeObsoletePreviews(widgetsAndShortcuts, packageUser);
-    }
-
-    private synchronized void setWidgetsAndShortcuts(ArrayList<WidgetItem> rawWidgetsShortcuts,
-            LauncherAppState app, @Nullable PackageUserKey packageUser) {
-        if (DEBUG) {
-            Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + rawWidgetsShortcuts.size());
-        }
-
-        // Temporary list 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<>();
-
-        // clear the lists.
-        if (packageUser == null) {
-            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();
-                    }
-                }
-            }
-        }
-
-        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);
-        }
-
-        // Update each package entry
-        IconCache iconCache = app.getIconCache();
-        for (PackageItemInfo p : tmpPackageItemInfos.values()) {
-            iconCache.getTitleAndIconForApp(p, true /* userLowResIcon */);
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/com/android/launcher3/notification/NotificationFooterLayout.java b/src/com/android/launcher3/notification/NotificationFooterLayout.java
index 1216a27..c7de5b0 100644
--- a/src/com/android/launcher3/notification/NotificationFooterLayout.java
+++ b/src/com/android/launcher3/notification/NotificationFooterLayout.java
@@ -29,7 +29,6 @@
 import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 
-import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.PropertyListBuilder;
@@ -151,15 +150,16 @@
 
     public void animateFirstNotificationTo(Rect toBounds,
             final IconAnimationEndListener callback) {
-        AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
+        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 = LauncherAnimUtils.ofPropertyValuesHolder(firstNotification,
-                new PropertyListBuilder().scale(scale).translationY(toBounds.top - fromBounds.top
-                        + (fromBounds.height() * scale - fromBounds.height()) / 2).build());
+        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) {
diff --git a/src/com/android/launcher3/notification/NotificationInfo.java b/src/com/android/launcher3/notification/NotificationInfo.java
index 6918935..e5525b2 100644
--- a/src/com/android/launcher3/notification/NotificationInfo.java
+++ b/src/com/android/launcher3/notification/NotificationInfo.java
@@ -30,13 +30,14 @@
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.dot.DotInfo;
 import com.android.launcher3.graphics.IconPalette;
 import com.android.launcher3.util.PackageUserKey;
 
 /**
  * An object that contains relevant information from a {@link StatusBarNotification}. This should
  * only be created when we need to show the notification contents on the UI; until then, a
- * {@link com.android.launcher3.badge.BadgeInfo} with only the notification key should
+ * {@link DotInfo} with only the notification key should
  * be passed around, and then this can be constructed using the StatusBarNotification from
  * {@link NotificationListener#getNotificationsForKeys(java.util.List)}.
  */
@@ -50,7 +51,6 @@
     public final boolean autoCancel;
     public final boolean dismissable;
 
-    private int mBadgeIcon;
     private Drawable mIconDrawable;
     private int mIconColor;
     private boolean mIsIconLarge;
@@ -65,10 +65,10 @@
         title = notification.extras.getCharSequence(Notification.EXTRA_TITLE);
         text = notification.extras.getCharSequence(Notification.EXTRA_TEXT);
 
-        mBadgeIcon = notification.getBadgeIconType();
+        int iconType = notification.getBadgeIconType();
         // Load the icon. Since it is backed by ashmem, we won't copy the entire bitmap
         // into our process as long as we don't touch it and it exists in systemui.
-        Icon icon = mBadgeIcon == Notification.BADGE_ICON_SMALL ? null : notification.getLargeIcon();
+        Icon icon = iconType == Notification.BADGE_ICON_SMALL ? null : notification.getLargeIcon();
         if (icon == null) {
             // Use the small icon.
             icon = notification.getSmallIcon();
@@ -84,7 +84,6 @@
             mIconDrawable = new BitmapDrawable(context.getResources(), LauncherAppState
                     .getInstance(context).getIconCache()
                     .getDefaultIcon(statusBarNotification.getUser()).icon);
-            mBadgeIcon = Notification.BADGE_ICON_NONE;
         }
         intent = notification.contentIntent;
         autoCancel = (notification.flags & Notification.FLAG_AUTO_CANCEL) != 0;
@@ -125,15 +124,4 @@
         icon.setTint(mIconColor);
         return icon;
     }
-
-    public boolean isIconLarge() {
-        return mIsIconLarge;
-    }
-
-    public boolean shouldShowIconInBadge() {
-        // If the icon we're using for this notification matches what the Notification
-        // specified should show in the badge, then return true.
-        return mIsIconLarge && mBadgeIcon == Notification.BADGE_ICON_LARGE
-                || !mIsIconLarge && mBadgeIcon == Notification.BADGE_ICON_SMALL;
-    }
 }
diff --git a/src/com/android/launcher3/notification/NotificationKeyData.java b/src/com/android/launcher3/notification/NotificationKeyData.java
index bf7ae1a..5050457 100644
--- a/src/com/android/launcher3/notification/NotificationKeyData.java
+++ b/src/com/android/launcher3/notification/NotificationKeyData.java
@@ -18,14 +18,15 @@
 
 import android.app.Notification;
 import android.service.notification.StatusBarNotification;
-import android.support.annotation.NonNull;
 
 import java.util.ArrayList;
 import java.util.List;
 
+import androidx.annotation.NonNull;
+
 /**
  * The key data associated with the notification, used to determine what to include
- * in badges and dummy popup views before they are populated.
+ * in dots and dummy popup views before they are populated.
  *
  * @see NotificationInfo for the full data used when populating the dummy views.
  */
diff --git a/src/com/android/launcher3/notification/NotificationListener.java b/src/com/android/launcher3/notification/NotificationListener.java
index b527b6a..e57a051 100644
--- a/src/com/android/launcher3/notification/NotificationListener.java
+++ b/src/com/android/launcher3/notification/NotificationListener.java
@@ -16,7 +16,7 @@
 
 package com.android.launcher3.notification;
 
-import static com.android.launcher3.SettingsActivity.NOTIFICATION_BADGING;
+import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver;
 
 import android.annotation.TargetApi;
 import android.app.Notification;
@@ -27,15 +27,14 @@
 import android.os.Message;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
-import android.support.annotation.Nullable;
 import android.text.TextUtils;
-import android.util.ArraySet;
 import android.util.Log;
 import android.util.Pair;
 
 import com.android.launcher3.LauncherModel;
+import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.PackageUserKey;
-import com.android.launcher3.util.SettingsObserver;
+import com.android.launcher3.util.SecureSettingsObserver;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -43,7 +42,8 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
+
+import androidx.annotation.Nullable;
 
 /**
  * A {@link NotificationListenerService} that sends updates to its
@@ -77,7 +77,7 @@
     /** The last notification key that was dismissed from launcher UI */
     private String mLastKeyDismissedByLauncher;
 
-    private SettingsObserver mNotificationBadgingObserver;
+    private SecureSettingsObserver mNotificationDotsObserver;
 
     private final Handler.Callback mWorkerCallback = new Handler.Callback() {
         @Override
@@ -169,7 +169,7 @@
         if (notificationListener != null) {
             notificationListener.onNotificationFullRefresh();
         } else if (!sIsCreated && sNotificationsChangedListener != null) {
-            // User turned off badging globally, so we unbound this service;
+            // User turned off dots globally, so we unbound this service;
             // tell the listener that there are no notifications to remove dots.
             sNotificationsChangedListener.onNotificationFullRefresh(
                     Collections.<StatusBarNotification>emptyList());
@@ -194,19 +194,20 @@
         super.onListenerConnected();
         sIsConnected = true;
 
-        mNotificationBadgingObserver = new SettingsObserver.Secure(getContentResolver()) {
-            @Override
-            public void onSettingChanged(boolean isNotificationBadgingEnabled) {
-                if (!isNotificationBadgingEnabled) {
-                    requestUnbind();
-                }
-            }
-        };
-        mNotificationBadgingObserver.register(NOTIFICATION_BADGING);
+        mNotificationDotsObserver =
+                newNotificationSettingsObserver(this, this::onNotificationSettingsChanged);
+        mNotificationDotsObserver.register();
+        mNotificationDotsObserver.dispatchOnChange();
 
         onNotificationFullRefresh();
     }
 
+    private void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) {
+        if (!areNotificationDotsEnabled && sIsConnected) {
+            requestUnbind();
+        }
+    }
+
     private void onNotificationFullRefresh() {
         mWorkerHandler.obtainMessage(MSG_NOTIFICATION_FULL_REFRESH).sendToTarget();
     }
@@ -215,7 +216,7 @@
     public void onListenerDisconnected() {
         super.onListenerDisconnected();
         sIsConnected = false;
-        mNotificationBadgingObserver.unregister();
+        mNotificationDotsObserver.unregister();
     }
 
     @Override
@@ -345,7 +346,7 @@
     private List<StatusBarNotification> filterNotifications(
             StatusBarNotification[] notifications) {
         if (notifications == null) return null;
-        Set<Integer> removedNotifications = new ArraySet<>();
+        IntSet removedNotifications = new IntSet();
         for (int i = 0; i < notifications.length; i++) {
             if (shouldBeFilteredOut(notifications[i])) {
                 removedNotifications.add(i);
diff --git a/src/com/android/launcher3/notification/NotificationMainView.java b/src/com/android/launcher3/notification/NotificationMainView.java
index 5c0e259..78627ec 100644
--- a/src/com/android/launcher3/notification/NotificationMainView.java
+++ b/src/com/android/launcher3/notification/NotificationMainView.java
@@ -179,7 +179,7 @@
 
 
     @Override
-    public boolean onDrag(float displacement, float velocity) {
+    public boolean onDrag(float displacement) {
         setContentTranslation(canChildBeDismissed()
                 ? displacement : OverScroll.dampedScroll(displacement, getWidth()));
         mContentTranslateAnimator.cancel();
diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
index be666a6..28000b9 100644
--- a/src/com/android/launcher3/popup/ArrowPopup.java
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.popup;
 
+import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
@@ -35,9 +37,10 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewOutlineProvider;
-import android.view.animation.AccelerateDecelerateInterpolator;
+import android.widget.FrameLayout;
 
 import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.InsettableFrameLayout;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.R;
@@ -47,6 +50,7 @@
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.graphics.TriangleShape;
 import com.android.launcher3.util.Themes;
+import com.android.launcher3.views.BaseDragLayer;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -63,7 +67,7 @@
     protected final Launcher mLauncher;
     protected final boolean mIsRtl;
 
-    private final int mArrayOffset;
+    private final int mArrowOffset;
     private final View mArrow;
 
     protected boolean mIsLeftAligned;
@@ -78,7 +82,7 @@
     public ArrowPopup(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         mInflater = LayoutInflater.from(context);
-        mOutlineRadius = getResources().getDimension(R.dimen.bg_round_rect_radius);
+        mOutlineRadius = Themes.getDialogCornerRadius(context);
         mLauncher = Launcher.getLauncher(context);
         mIsRtl = Utilities.isRtl(getResources());
 
@@ -96,7 +100,7 @@
         final int arrowHeight = resources.getDimensionPixelSize(R.dimen.popup_arrow_height);
         mArrow = new View(context);
         mArrow.setLayoutParams(new DragLayer.LayoutParams(arrowWidth, arrowHeight));
-        mArrayOffset = resources.getDimensionPixelSize(R.dimen.popup_arrow_vertical_offset);
+        mArrowOffset = resources.getDimensionPixelSize(R.dimen.popup_arrow_vertical_offset);
     }
 
     public ArrowPopup(Context context, AttributeSet attrs) {
@@ -122,6 +126,12 @@
         return (T) view;
     }
 
+    public <T extends View> T inflateAndAdd(int resId, ViewGroup container, int index) {
+        View view = mInflater.inflate(resId, container, false);
+        container.addView(view, index);
+        return (T) view;
+    }
+
     /**
      * Called when all view inflation and reordering in complete.
      */
@@ -134,7 +144,7 @@
     protected void reorderAndShow(int viewsToFlip) {
         setVisibility(View.INVISIBLE);
         mIsOpen = true;
-        mLauncher.getDragLayer().addView(this);
+        getPopupContainer().addView(this);
         orientAboutObject();
 
         boolean reverseOrder = mIsAboveIcon;
@@ -163,7 +173,7 @@
                 ? 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;
-        mLauncher.getDragLayer().addView(mArrow);
+        getPopupContainer().addView(mArrow);
         DragLayer.LayoutParams arrowLp = (DragLayer.LayoutParams) mArrow.getLayoutParams();
         if (mIsLeftAligned) {
             mArrow.setX(getX() + arrowCenterOffset - halfArrowWidth);
@@ -179,16 +189,22 @@
             ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create(
                     arrowLp.width, arrowLp.height, !mIsAboveIcon));
             Paint arrowPaint = arrowDrawable.getPaint();
-            arrowPaint.setColor(Themes.getAttrColor(mLauncher, R.attr.popupColorPrimary));
+            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());
         }
 
         mArrow.setPivotX(arrowLp.width / 2);
-        mArrow.setPivotY(mIsAboveIcon ? 0 : arrowLp.height);
+        mArrow.setPivotY(mIsAboveIcon ? arrowLp.height : 0);
 
         animateOpen();
     }
@@ -217,12 +233,12 @@
     protected void orientAboutObject() {
         measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
         int width = getMeasuredWidth();
-        int extraVerticalSpace = mArrow.getLayoutParams().height + mArrayOffset
+        int extraVerticalSpace = mArrow.getLayoutParams().height + mArrowOffset
                 + getResources().getDimensionPixelSize(R.dimen.popup_vertical_padding);
         int height = getMeasuredHeight() + extraVerticalSpace;
 
         getTargetObjectLocation(mTempRect);
-        DragLayer dragLayer = mLauncher.getDragLayer();
+        InsettableFrameLayout dragLayer = getPopupContainer();
         Rect insets = dragLayer.getInsets();
 
         // Align left (right in RTL) if there is room.
@@ -301,17 +317,16 @@
             return;
         }
 
-        DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
-        DragLayer.LayoutParams arrowLp = (DragLayer.LayoutParams) mArrow.getLayoutParams();
+        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
+        FrameLayout.LayoutParams arrowLp = (FrameLayout.LayoutParams) mArrow.getLayoutParams();
         if (mIsAboveIcon) {
             arrowLp.gravity = lp.gravity = Gravity.BOTTOM;
-            lp.bottomMargin =
-                    mLauncher.getDragLayer().getHeight() - y - getMeasuredHeight() - insets.top;
-            arrowLp.bottomMargin = lp.bottomMargin - arrowLp.height - mArrayOffset - insets.bottom;
+            lp.bottomMargin = getPopupContainer().getHeight() - y - getMeasuredHeight() - insets.top;
+            arrowLp.bottomMargin = lp.bottomMargin - arrowLp.height - mArrowOffset - insets.bottom;
         } else {
             arrowLp.gravity = lp.gravity = Gravity.TOP;
             lp.topMargin = y + insets.top;
-            arrowLp.topMargin = lp.topMargin - insets.top - arrowLp.height - mArrayOffset;
+            arrowLp.topMargin = lp.topMargin - insets.top - arrowLp.height - mArrowOffset;
         }
     }
 
@@ -320,7 +335,7 @@
         super.onLayout(changed, l, t, r, b);
 
         // enforce contained is within screen
-        DragLayer dragLayer = mLauncher.getDragLayer();
+        ViewGroup dragLayer = getPopupContainer();
         if (getTranslationX() + l < 0 || getTranslationX() + r > dragLayer.getWidth()) {
             // If we are still off screen, center horizontally too.
             mGravity |= Gravity.CENTER_HORIZONTAL;
@@ -338,10 +353,11 @@
     private void animateOpen() {
         setVisibility(View.VISIBLE);
 
-        final AnimatorSet openAnim = LauncherAnimUtils.createAnimatorSet();
+        final AnimatorSet openAnim = new AnimatorSet();
         final Resources res = getResources();
         final long revealDuration = (long) res.getInteger(R.integer.config_popupOpenCloseDuration);
-        final TimeInterpolator revealInterpolator = new AccelerateDecelerateInterpolator();
+        final long arrowDuration = res.getInteger(R.integer.config_popupArrowOpenCloseDuration);
+        final TimeInterpolator revealInterpolator = ACCEL_DEACCEL;
 
         // Rectangular reveal.
         final ValueAnimator revealAnim = createOpenCloseOutlineProvider()
@@ -349,27 +365,33 @@
         revealAnim.setDuration(revealDuration);
         revealAnim.setInterpolator(revealInterpolator);
 
-        Animator fadeIn = ObjectAnimator.ofFloat(this, ALPHA, 0, 1);
-        fadeIn.setDuration(revealDuration);
+        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(res.getInteger(R.integer.config_popupArrowOpenDuration));
+                .setDuration(arrowDuration);
 
         openAnim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
+                setAlpha(1f);
                 announceAccessibilityChanges();
                 mOpenCloseAnimator = null;
             }
         });
 
         mOpenCloseAnimator = openAnim;
-        openAnim.playSequentially(revealAnim, arrowScale);
+        openAnim.playSequentially(arrowScale, revealAnim);
         openAnim.start();
     }
 
@@ -386,26 +408,35 @@
         }
         mIsOpen = false;
 
-        final AnimatorSet closeAnim = LauncherAnimUtils.createAnimatorSet();
-        // Hide the arrow
-        closeAnim.play(ObjectAnimator.ofFloat(mArrow, LauncherAnimUtils.SCALE_PROPERTY, 0));
-        closeAnim.play(ObjectAnimator.ofFloat(mArrow, ALPHA, 0));
 
+        final AnimatorSet closeAnim = new AnimatorSet();
         final Resources res = getResources();
-        final TimeInterpolator revealInterpolator = new AccelerateDecelerateInterpolator();
+        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.play(revealAnim);
+        closeAnim.playSequentially(revealAnim, scaleArrow);
 
-        Animator fadeOut = ObjectAnimator.ofFloat(this, ALPHA, 0);
+        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.setDuration((long) res.getInteger(R.integer.config_popupOpenCloseDuration));
         closeAnim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
@@ -427,21 +458,25 @@
     protected void onCreateCloseAnimation(AnimatorSet anim) { }
 
     private RoundedRectRevealOutlineProvider createOpenCloseOutlineProvider() {
-        int arrowCenterX = getResources().getDimensionPixelSize(mIsLeftAligned ^ mIsRtl ?
+        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, arrowCenterY, arrowCenterX, arrowCenterY);
+        mStartRect.set(arrowCenterX - halfArrowWidth, arrowCenterY, arrowCenterX + halfArrowWidth,
+                arrowCenterY);
         if (mEndRect.isEmpty()) {
             mEndRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
         }
 
         return new RoundedRectRevealOutlineProvider
-                (mOutlineRadius, mOutlineRadius, mStartRect, mEndRect);
+                (arrowCornerRadius, mOutlineRadius, mStartRect, mEndRect);
     }
 
     /**
@@ -454,7 +489,11 @@
         }
         mIsOpen = false;
         mDeferContainerRemoval = false;
-        mLauncher.getDragLayer().removeView(this);
-        mLauncher.getDragLayer().removeView(mArrow);
+        getPopupContainer().removeView(this);
+        getPopupContainer().removeView(mArrow);
+    }
+
+    protected BaseDragLayer getPopupContainer() {
+        return mLauncher.getDragLayer();
     }
 }
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 172cf41..9719a18 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.popup;
 
+import static com.android.launcher3.Utilities.squaredHypot;
+import static com.android.launcher3.Utilities.squaredTouchSlop;
 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;
@@ -34,10 +36,10 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.util.Pair;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.widget.ImageView;
 
@@ -51,39 +53,42 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherModel;
 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.badge.BadgeInfo;
+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.logging.LoggerUtils;
 import com.android.launcher3.notification.NotificationInfo;
 import com.android.launcher3.notification.NotificationItemView;
 import com.android.launcher3.notification.NotificationKeyData;
+import com.android.launcher3.popup.PopupDataProvider.PopupDataChangeListener;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.shortcuts.DeepShortcutView;
 import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
+import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.touch.ItemLongClickListener;
 import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.views.BaseDragLayer;
 
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
+import java.util.function.Predicate;
 
 /**
  * A container for shortcuts to deep links and notifications associated with an app.
  */
-@TargetApi(Build.VERSION_CODES.N)
 public class PopupContainerWithArrow extends ArrowPopup implements DragSource,
         DragController.DragListener, View.OnLongClickListener,
-        View.OnTouchListener {
+        View.OnTouchListener, PopupDataChangeListener {
 
     private final List<DeepShortcutView> mShortcuts = new ArrayList<>();
     private final PointF mInterceptTouchDown = new PointF();
-    private final Point mIconLastTouchPos = new Point();
+    protected final Point mIconLastTouchPos = new Point();
 
     private final int mStartDragThreshold;
     private final LauncherAccessibilityDelegate mAccessibilityDelegate;
@@ -114,6 +119,18 @@
     }
 
     @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mLauncher.getPopupDataProvider().setChangeListener(this);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        mLauncher.getPopupDataProvider().setChangeListener(null);
+    }
+
+    @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
             mInterceptTouchDown.set(ev.getX(), ev.getY());
@@ -123,8 +140,8 @@
             return true;
         }
         // Stop sending touch events to deep shortcut views if user moved beyond touch slop.
-        return Math.hypot(mInterceptTouchDown.x - ev.getX(), mInterceptTouchDown.y - ev.getY())
-                > ViewConfiguration.get(getContext()).getScaledTouchSlop();
+        return squaredHypot(mInterceptTouchDown.x - ev.getX(), mInterceptTouchDown.y - ev.getY())
+                > squaredTouchSlop(getContext());
     }
 
     @Override
@@ -143,13 +160,22 @@
     @Override
     public void logActionCommand(int command) {
         mLauncher.getUserEventDispatcher().logActionCommand(
-                command, mOriginalIcon, ContainerType.DEEPSHORTCUTS);
+                command, mOriginalIcon, getLogContainerType());
+    }
+
+    @Override
+    public int getLogContainerType() {
+        return ContainerType.DEEPSHORTCUTS;
+    }
+
+    public OnClickListener getItemClickListener() {
+        return ItemClickHandler.INSTANCE;
     }
 
     @Override
     public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            DragLayer dl = mLauncher.getDragLayer();
+            BaseDragLayer dl = getPopupContainer();
             if (!dl.isEventOverView(this, ev)) {
                 mLauncher.getUserEventDispatcher().logActionTapOutside(
                         LoggerUtils.newContainerTarget(ContainerType.DEEPSHORTCUTS));
@@ -179,17 +205,10 @@
             return null;
         }
 
-        PopupDataProvider popupDataProvider = launcher.getPopupDataProvider();
-        List<String> shortcutIds = popupDataProvider.getShortcutIdsForItem(itemInfo);
-        List<NotificationKeyData> notificationKeys = popupDataProvider
-                .getNotificationKeysForItem(itemInfo);
-        List<SystemShortcut> systemShortcuts = popupDataProvider
-                .getEnabledSystemShortcutsForItem(itemInfo);
-
         final PopupContainerWithArrow container =
                 (PopupContainerWithArrow) launcher.getLayoutInflater().inflate(
                         R.layout.popup_container, launcher.getDragLayer(), false);
-        container.populateAndShow(icon, shortcutIds, notificationKeys, systemShortcuts);
+        container.populateAndShow(icon, itemInfo, SystemShortcutFactory.INSTANCE.get(launcher));
         return container;
     }
 
@@ -214,8 +233,21 @@
         }
     }
 
+    protected void populateAndShow(
+            BubbleTextView icon, ItemInfo item, SystemShortcutFactory factory) {
+        PopupDataProvider popupDataProvider = mLauncher.getPopupDataProvider();
+        populateAndShow(icon,
+                popupDataProvider.getShortcutCountForItem(item),
+                popupDataProvider.getNotificationKeysForItem(item),
+                factory.getEnabledShortcuts(mLauncher, item));
+    }
+
+    public ViewGroup getSystemShortcutContainerForTesting() {
+        return mSystemShortcutContainer;
+    }
+
     @TargetApi(Build.VERSION_CODES.P)
-    private void populateAndShow(final BubbleTextView originalIcon, final List<String> shortcutIds,
+    protected void populateAndShow(final BubbleTextView originalIcon, int shortcutCount,
             final List<NotificationKeyData> notificationKeys, List<SystemShortcut> systemShortcuts) {
         mNumNotifications = notificationKeys.size();
         mOriginalIcon = originalIcon;
@@ -233,12 +265,12 @@
         int viewsToFlip = getChildCount();
         mSystemShortcutContainer = this;
 
-        if (!shortcutIds.isEmpty()) {
+        if (shortcutCount > 0) {
             if (mNotificationItemView != null) {
                 mNotificationItemView.addGutter();
             }
 
-            for (int i = shortcutIds.size(); i > 0; i--) {
+            for (int i = shortcutCount; i > 0; i--) {
                 mShortcuts.add(inflateAndAdd(R.layout.deep_shortcut, this));
             }
             updateHiddenShortcuts();
@@ -268,7 +300,7 @@
         }
 
         mLauncher.getDragController().addDragListener(this);
-        mOriginalIcon.forceHideBadge(true);
+        mOriginalIcon.forceHideDot(true);
 
         // All views are added. Animate layout from now on.
         setLayoutTransition(new LayoutTransition());
@@ -277,7 +309,7 @@
         final Looper workerLooper = LauncherModel.getWorkerLooper();
         new Handler(workerLooper).postAtFrontOfQueue(PopupPopulator.createUpdateRunnable(
                 mLauncher, originalItemInfo, new Handler(Looper.getMainLooper()),
-                this, shortcutIds, mShortcuts, notificationKeys));
+                this, mShortcuts, notificationKeys));
     }
 
     private String getTitleForAccessibility() {
@@ -293,7 +325,7 @@
 
     @Override
     protected void getTargetObjectLocation(Rect outPos) {
-        mLauncher.getDragLayer().getDescendantRectRelativeToSelf(mOriginalIcon, outPos);
+        getPopupContainer().getDescendantRectRelativeToSelf(mOriginalIcon, outPos);
         outPos.top += mOriginalIcon.getPaddingTop();
         outPos.left += mOriginalIcon.getPaddingLeft();
         outPos.right -= mOriginalIcon.getPaddingRight();
@@ -341,7 +373,7 @@
     }
 
     @Override
-    protected void onWidgetsBound() {
+    public void onWidgetsBound() {
         ItemInfo itemInfo = (ItemInfo) mOriginalIcon.getTag();
         SystemShortcut widgetInfo = new SystemShortcut.Widgets();
         View.OnClickListener onClickListener = widgetInfo.getOnClickListener(mLauncher, itemInfo);
@@ -380,17 +412,15 @@
     }
 
     private void initializeSystemShortcut(int resId, ViewGroup container, SystemShortcut info) {
-        View view = inflateAndAdd(resId, container);
+        View view = inflateAndAdd(
+                resId, container, getInsertIndexForSystemShortcut(container, info));
         if (view instanceof DeepShortcutView) {
             // Expanded system shortcut, with both icon and text shown on white background.
             final DeepShortcutView shortcutView = (DeepShortcutView) view;
-            shortcutView.getIconView().setBackgroundResource(info.iconResId);
-            shortcutView.getBubbleText().setText(info.labelResId);
+            info.setIconAndLabelFor(shortcutView.getIconView(), shortcutView.getBubbleText());
         } else if (view instanceof ImageView) {
             // Only the system shortcut icon shows on a gray background header.
-            final ImageView shortcutIcon = (ImageView) view;
-            shortcutIcon.setImageResource(info.iconResId);
-            shortcutIcon.setContentDescription(getContext().getText(info.labelResId));
+            info.setIconAndContentDescriptionFor((ImageView) view);
         }
         view.setTag(info);
         view.setOnClickListener(info.getOnClickListener(mLauncher,
@@ -398,6 +428,17 @@
     }
 
     /**
+     * Returns an index for inserting a shortcut into a container.
+     */
+    private int getInsertIndexForSystemShortcut(ViewGroup container, SystemShortcut shortcut) {
+        final View separator = container.findViewById(R.id.separator);
+
+        return separator != null && shortcut.isLeftGroup() ?
+                container.indexOfChild(separator) :
+                container.getChildCount();
+    }
+
+    /**
      * Determines when the deferred drag should be started.
      *
      * Current behavior:
@@ -408,6 +449,11 @@
 
             @Override
             public boolean shouldStartDrag(double distanceDragged) {
+                if (TestProtocol.sDebugTracing) {
+                    Log.d(TestProtocol.NO_DRAG_TAG,
+                            "createPreDragCondition().shouldStartDrag " + distanceDragged + ", "
+                                    + mStartDragThreshold);
+                }
                 return distanceDragged > mStartDragThreshold;
             }
 
@@ -442,32 +488,34 @@
     }
 
     /**
-     * Updates the notification header if the original icon's badge updated.
+     * Updates the notification header if the original icon's dot updated.
      */
-    public void updateNotificationHeader(Set<PackageUserKey> updatedBadges) {
+    @Override
+    public void onNotificationDotsUpdated(Predicate<PackageUserKey> updatedDots) {
         ItemInfo itemInfo = (ItemInfo) mOriginalIcon.getTag();
         PackageUserKey packageUser = PackageUserKey.fromItemInfo(itemInfo);
-        if (updatedBadges.contains(packageUser)) {
+        if (updatedDots.test(packageUser)) {
             updateNotificationHeader();
         }
     }
 
     private void updateNotificationHeader() {
         ItemInfoWithIcon itemInfo = (ItemInfoWithIcon) mOriginalIcon.getTag();
-        BadgeInfo badgeInfo = mLauncher.getBadgeInfoForItem(itemInfo);
-        if (mNotificationItemView != null && badgeInfo != null) {
+        DotInfo dotInfo = mLauncher.getDotInfoForItem(itemInfo);
+        if (mNotificationItemView != null && dotInfo != null) {
             mNotificationItemView.updateHeader(
-                    badgeInfo.getNotificationCount(), itemInfo.iconColor);
+                    dotInfo.getNotificationCount(), itemInfo.iconColor);
         }
     }
 
-    public void trimNotifications(Map<PackageUserKey, BadgeInfo> updatedBadges) {
+    @Override
+    public void trimNotifications(Map<PackageUserKey, DotInfo> updatedDots) {
         if (mNotificationItemView == null) {
             return;
         }
         ItemInfo originalInfo = (ItemInfo) mOriginalIcon.getTag();
-        BadgeInfo badgeInfo = updatedBadges.get(PackageUserKey.fromItemInfo(originalInfo));
-        if (badgeInfo == null || badgeInfo.getNotificationKeys().size() == 0) {
+        DotInfo dotInfo = updatedDots.get(PackageUserKey.fromItemInfo(originalInfo));
+        if (dotInfo == null || dotInfo.getNotificationKeys().size() == 0) {
             // No more notifications, remove the notification views and expand all shortcuts.
             mNotificationItemView.removeAllViews();
             mNotificationItemView = null;
@@ -475,7 +523,7 @@
             updateDividers();
         } else {
             mNotificationItemView.trimNotifications(
-                    NotificationKeyData.extractKeysOnly(badgeInfo.getNotificationKeys()));
+                    NotificationKeyData.extractKeysOnly(dotInfo.getNotificationKeys()));
         }
     }
 
@@ -520,14 +568,14 @@
     protected void onCreateCloseAnimation(AnimatorSet anim) {
         // Animate original icon's text back in.
         anim.play(mOriginalIcon.createTextAlphaAnimator(true /* fadeIn */));
-        mOriginalIcon.forceHideBadge(false);
+        mOriginalIcon.forceHideDot(false);
     }
 
     @Override
     protected void closeComplete() {
         super.closeComplete();
         mOriginalIcon.setTextVisibility(mOriginalIcon.shouldTextBeVisible());
-        mOriginalIcon.forceHideBadge(false);
+        mOriginalIcon.forceHideDot(false);
     }
 
     @Override
diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java
index f1b8ec0..2d301ac 100644
--- a/src/com/android/launcher3/popup/PopupDataProvider.java
+++ b/src/com/android/launcher3/popup/PopupDataProvider.java
@@ -18,19 +18,16 @@
 
 import android.content.ComponentName;
 import android.service.notification.StatusBarNotification;
-import android.support.annotation.NonNull;
 import android.util.Log;
 
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.badge.BadgeInfo;
+import com.android.launcher3.dot.DotInfo;
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.notification.NotificationKeyData;
 import com.android.launcher3.notification.NotificationListener;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.widget.WidgetListRowEntry;
 
@@ -40,6 +37,9 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.function.Predicate;
+
+import androidx.annotation.NonNull;
 
 /**
  * Provides data for the popup menu that appears after long-clicking on apps.
@@ -49,138 +49,135 @@
     private static final boolean LOGD = false;
     private static final String TAG = "PopupDataProvider";
 
-    /** Note that these are in order of priority. */
-    private static final SystemShortcut[] SYSTEM_SHORTCUTS = new SystemShortcut[] {
-            new SystemShortcut.AppInfo(),
-            new SystemShortcut.Widgets(),
-            new SystemShortcut.Install()
-    };
-
     private final Launcher mLauncher;
 
-    /** Maps launcher activity components to their list of shortcut ids. */
-    private MultiHashMap<ComponentKey, String> mDeepShortcutMap = new MultiHashMap<>();
-    /** Maps packages to their BadgeInfo's . */
-    private Map<PackageUserKey, BadgeInfo> mPackageUserToBadgeInfos = new HashMap<>();
+    /** Maps launcher activity components to a count of how many shortcuts they have. */
+    private HashMap<ComponentKey, Integer> mDeepShortcutMap = new HashMap<>();
+    /** Maps packages to their DotInfo's . */
+    private Map<PackageUserKey, DotInfo> mPackageUserToDotInfos = new HashMap<>();
     /** Maps packages to their Widgets */
     private ArrayList<WidgetListRowEntry> mAllWidgets = new ArrayList<>();
 
+    private PopupDataChangeListener mChangeListener = PopupDataChangeListener.INSTANCE;
+
     public PopupDataProvider(Launcher launcher) {
         mLauncher = launcher;
     }
 
+    private void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
+        mLauncher.updateNotificationDots(updatedDots);
+        mChangeListener.onNotificationDotsUpdated(updatedDots);
+    }
+
     @Override
     public void onNotificationPosted(PackageUserKey postedPackageUserKey,
             NotificationKeyData notificationKey, boolean shouldBeFilteredOut) {
-        BadgeInfo badgeInfo = mPackageUserToBadgeInfos.get(postedPackageUserKey);
-        boolean badgeShouldBeRefreshed;
-        if (badgeInfo == null) {
+        DotInfo dotInfo = mPackageUserToDotInfos.get(postedPackageUserKey);
+        boolean dotShouldBeRefreshed;
+        if (dotInfo == null) {
             if (!shouldBeFilteredOut) {
-                BadgeInfo newBadgeInfo = new BadgeInfo(postedPackageUserKey);
-                newBadgeInfo.addOrUpdateNotificationKey(notificationKey);
-                mPackageUserToBadgeInfos.put(postedPackageUserKey, newBadgeInfo);
-                badgeShouldBeRefreshed = true;
+                DotInfo newDotInfo = new DotInfo();
+                newDotInfo.addOrUpdateNotificationKey(notificationKey);
+                mPackageUserToDotInfos.put(postedPackageUserKey, newDotInfo);
+                dotShouldBeRefreshed = true;
             } else {
-                badgeShouldBeRefreshed = false;
+                dotShouldBeRefreshed = false;
             }
         } else {
-            badgeShouldBeRefreshed = shouldBeFilteredOut
-                    ? badgeInfo.removeNotificationKey(notificationKey)
-                    : badgeInfo.addOrUpdateNotificationKey(notificationKey);
-            if (badgeInfo.getNotificationKeys().size() == 0) {
-                mPackageUserToBadgeInfos.remove(postedPackageUserKey);
+            dotShouldBeRefreshed = shouldBeFilteredOut
+                    ? dotInfo.removeNotificationKey(notificationKey)
+                    : dotInfo.addOrUpdateNotificationKey(notificationKey);
+            if (dotInfo.getNotificationKeys().size() == 0) {
+                mPackageUserToDotInfos.remove(postedPackageUserKey);
             }
         }
-        if (badgeShouldBeRefreshed) {
-            mLauncher.updateIconBadges(Utilities.singletonHashSet(postedPackageUserKey));
+        if (dotShouldBeRefreshed) {
+            updateNotificationDots(t -> postedPackageUserKey.equals(t));
         }
     }
 
     @Override
     public void onNotificationRemoved(PackageUserKey removedPackageUserKey,
             NotificationKeyData notificationKey) {
-        BadgeInfo oldBadgeInfo = mPackageUserToBadgeInfos.get(removedPackageUserKey);
-        if (oldBadgeInfo != null && oldBadgeInfo.removeNotificationKey(notificationKey)) {
-            if (oldBadgeInfo.getNotificationKeys().size() == 0) {
-                mPackageUserToBadgeInfos.remove(removedPackageUserKey);
+        DotInfo oldDotInfo = mPackageUserToDotInfos.get(removedPackageUserKey);
+        if (oldDotInfo != null && oldDotInfo.removeNotificationKey(notificationKey)) {
+            if (oldDotInfo.getNotificationKeys().size() == 0) {
+                mPackageUserToDotInfos.remove(removedPackageUserKey);
             }
-            mLauncher.updateIconBadges(Utilities.singletonHashSet(removedPackageUserKey));
-            trimNotifications(mPackageUserToBadgeInfos);
+            updateNotificationDots(t -> removedPackageUserKey.equals(t));
+            trimNotifications(mPackageUserToDotInfos);
         }
     }
 
     @Override
     public void onNotificationFullRefresh(List<StatusBarNotification> activeNotifications) {
         if (activeNotifications == null) return;
-        // This will contain the PackageUserKeys which have updated badges.
-        HashMap<PackageUserKey, BadgeInfo> updatedBadges = new HashMap<>(mPackageUserToBadgeInfos);
-        mPackageUserToBadgeInfos.clear();
+        // This will contain the PackageUserKeys which have updated dots.
+        HashMap<PackageUserKey, DotInfo> updatedDots = new HashMap<>(mPackageUserToDotInfos);
+        mPackageUserToDotInfos.clear();
         for (StatusBarNotification notification : activeNotifications) {
             PackageUserKey packageUserKey = PackageUserKey.fromNotification(notification);
-            BadgeInfo badgeInfo = mPackageUserToBadgeInfos.get(packageUserKey);
-            if (badgeInfo == null) {
-                badgeInfo = new BadgeInfo(packageUserKey);
-                mPackageUserToBadgeInfos.put(packageUserKey, badgeInfo);
+            DotInfo dotInfo = mPackageUserToDotInfos.get(packageUserKey);
+            if (dotInfo == null) {
+                dotInfo = new DotInfo();
+                mPackageUserToDotInfos.put(packageUserKey, dotInfo);
             }
-            badgeInfo.addOrUpdateNotificationKey(NotificationKeyData
-                    .fromNotification(notification));
+            dotInfo.addOrUpdateNotificationKey(NotificationKeyData.fromNotification(notification));
         }
 
-        // Add and remove from updatedBadges so it contains the PackageUserKeys of updated badges.
-        for (PackageUserKey packageUserKey : mPackageUserToBadgeInfos.keySet()) {
-            BadgeInfo prevBadge = updatedBadges.get(packageUserKey);
-            BadgeInfo newBadge = mPackageUserToBadgeInfos.get(packageUserKey);
-            if (prevBadge == null) {
-                updatedBadges.put(packageUserKey, newBadge);
+        // Add and remove from updatedDots so it contains the PackageUserKeys of updated dots.
+        for (PackageUserKey packageUserKey : mPackageUserToDotInfos.keySet()) {
+            DotInfo prevDot = updatedDots.get(packageUserKey);
+            DotInfo newDot = mPackageUserToDotInfos.get(packageUserKey);
+            if (prevDot == null) {
+                updatedDots.put(packageUserKey, newDot);
             } else {
-                if (!prevBadge.shouldBeInvalidated(newBadge)) {
-                    updatedBadges.remove(packageUserKey);
-                }
+                // No need to update the dot if it already existed (no visual change).
+                // Note that if the dot was removed entirely, we wouldn't reach this point because
+                // this loop only includes active notifications added above.
+                updatedDots.remove(packageUserKey);
             }
         }
 
-        if (!updatedBadges.isEmpty()) {
-            mLauncher.updateIconBadges(updatedBadges.keySet());
+        if (!updatedDots.isEmpty()) {
+            updateNotificationDots(updatedDots::containsKey);
         }
-        trimNotifications(updatedBadges);
+        trimNotifications(updatedDots);
     }
 
-    private void trimNotifications(Map<PackageUserKey, BadgeInfo> updatedBadges) {
-        PopupContainerWithArrow openContainer = PopupContainerWithArrow.getOpen(mLauncher);
-        if (openContainer != null) {
-            openContainer.trimNotifications(updatedBadges);
-        }
+    private void trimNotifications(Map<PackageUserKey, DotInfo> updatedDots) {
+        mChangeListener.trimNotifications(updatedDots);
     }
 
-    public void setDeepShortcutMap(MultiHashMap<ComponentKey, String> deepShortcutMapCopy) {
+    public void setDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMapCopy) {
         mDeepShortcutMap = deepShortcutMapCopy;
         if (LOGD) Log.d(TAG, "bindDeepShortcutMap: " + mDeepShortcutMap);
     }
 
-    public List<String> getShortcutIdsForItem(ItemInfo info) {
+    public int getShortcutCountForItem(ItemInfo info) {
         if (!DeepShortcutManager.supportsShortcuts(info)) {
-            return Collections.EMPTY_LIST;
+            return 0;
         }
         ComponentName component = info.getTargetComponent();
         if (component == null) {
-            return Collections.EMPTY_LIST;
+            return 0;
         }
 
-        List<String> ids = mDeepShortcutMap.get(new ComponentKey(component, info.user));
-        return ids == null ? Collections.EMPTY_LIST : ids;
+        Integer count = mDeepShortcutMap.get(new ComponentKey(component, info.user));
+        return count == null ? 0 : count;
     }
 
-    public BadgeInfo getBadgeInfoForItem(ItemInfo info) {
+    public DotInfo getDotInfoForItem(ItemInfo info) {
         if (!DeepShortcutManager.supportsShortcuts(info)) {
             return null;
         }
 
-        return mPackageUserToBadgeInfos.get(PackageUserKey.fromItemInfo(info));
+        return mPackageUserToDotInfos.get(PackageUserKey.fromItemInfo(info));
     }
 
     public @NonNull List<NotificationKeyData> getNotificationKeysForItem(ItemInfo info) {
-        BadgeInfo badgeInfo = getBadgeInfoForItem(info);
-        return badgeInfo == null ? Collections.EMPTY_LIST : badgeInfo.getNotificationKeys();
+        DotInfo dotInfo = getDotInfoForItem(info);
+        return dotInfo == null ? Collections.EMPTY_LIST : dotInfo.getNotificationKeys();
     }
 
     /** This makes a potentially expensive binder call and should be run on a background thread. */
@@ -191,16 +188,6 @@
                 : notificationListener.getNotificationsForKeys(notificationKeys);
     }
 
-    public @NonNull List<SystemShortcut> getEnabledSystemShortcutsForItem(ItemInfo info) {
-        List<SystemShortcut> systemShortcuts = new ArrayList<>();
-        for (SystemShortcut systemShortcut : SYSTEM_SHORTCUTS) {
-            if (systemShortcut.getOnClickListener(mLauncher, info) != null) {
-                systemShortcuts.add(systemShortcut);
-            }
-        }
-        return systemShortcuts;
-    }
-
     public void cancelNotification(String notificationKey) {
         NotificationListener notificationListener = NotificationListener.getInstanceIfConnected();
         if (notificationListener == null) {
@@ -211,6 +198,11 @@
 
     public void setAllWidgets(ArrayList<WidgetListRowEntry> allWidgets) {
         mAllWidgets = allWidgets;
+        mChangeListener.onWidgetsBound();
+    }
+
+    public void setChangeListener(PopupDataChangeListener listener) {
+        mChangeListener = listener == null ? PopupDataChangeListener.INSTANCE : listener;
     }
 
     public ArrayList<WidgetListRowEntry> getAllWidgets() {
@@ -233,4 +225,15 @@
         }
         return null;
     }
+
+    public interface PopupDataChangeListener {
+
+        PopupDataChangeListener INSTANCE = new PopupDataChangeListener() { };
+
+        default void onNotificationDotsUpdated(Predicate<PackageUserKey> updatedDots) { }
+
+        default void trimNotifications(Map<PackageUserKey, DotInfo> updatedDots) { }
+
+        default void onWidgetsBound() { }
+    }
 }
diff --git a/src/com/android/launcher3/popup/PopupPopulator.java b/src/com/android/launcher3/popup/PopupPopulator.java
index b295bb2..dbfe988 100644
--- a/src/com/android/launcher3/popup/PopupPopulator.java
+++ b/src/com/android/launcher3/popup/PopupPopulator.java
@@ -17,21 +17,19 @@
 package com.android.launcher3.popup;
 
 import android.content.ComponentName;
+import android.content.pm.ShortcutInfo;
 import android.os.Handler;
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
-import android.support.annotation.Nullable;
-import android.support.annotation.VisibleForTesting;
 
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.graphics.LauncherIcons;
+import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.notification.NotificationInfo;
 import com.android.launcher3.notification.NotificationKeyData;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.shortcuts.DeepShortcutView;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.util.PackageUserKey;
 
 import java.util.ArrayList;
@@ -40,6 +38,9 @@
 import java.util.Iterator;
 import java.util.List;
 
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
 /**
  * Contains logic relevant to populating a {@link PopupContainerWithArrow}. In particular,
  * this class determines which items appear in the container, and in what order.
@@ -53,10 +54,10 @@
     /**
      * Sorts shortcuts in rank order, with manifest shortcuts coming before dynamic shortcuts.
      */
-    private static final Comparator<ShortcutInfoCompat> SHORTCUT_RANK_COMPARATOR
-            = new Comparator<ShortcutInfoCompat>() {
+    private static final Comparator<ShortcutInfo> SHORTCUT_RANK_COMPARATOR
+            = new Comparator<ShortcutInfo>() {
         @Override
-        public int compare(ShortcutInfoCompat a, ShortcutInfoCompat b) {
+        public int compare(ShortcutInfo a, ShortcutInfo b) {
             if (a.isDeclaredInManifest() && !b.isDeclaredInManifest()) {
                 return -1;
             }
@@ -75,11 +76,11 @@
      * @param shortcutIdToRemoveFirst An id that should be filtered out first, if any.
      * @return a subset of shortcuts, in sorted order, with size <= MAX_SHORTCUTS.
      */
-    public static List<ShortcutInfoCompat> sortAndFilterShortcuts(
-            List<ShortcutInfoCompat> shortcuts, @Nullable String shortcutIdToRemoveFirst) {
+    public static List<ShortcutInfo> sortAndFilterShortcuts(
+            List<ShortcutInfo> shortcuts, @Nullable String shortcutIdToRemoveFirst) {
         // Remove up to one specific shortcut before sorting and doing somewhat fancy filtering.
         if (shortcutIdToRemoveFirst != null) {
-            Iterator<ShortcutInfoCompat> shortcutIterator = shortcuts.iterator();
+            Iterator<ShortcutInfo> shortcutIterator = shortcuts.iterator();
             while (shortcutIterator.hasNext()) {
                 if (shortcutIterator.next().getId().equals(shortcutIdToRemoveFirst)) {
                     shortcutIterator.remove();
@@ -95,11 +96,11 @@
 
         // The list of shortcuts is now sorted with static shortcuts followed by dynamic
         // shortcuts. We want to preserve this order, but only keep MAX_SHORTCUTS.
-        List<ShortcutInfoCompat> filteredShortcuts = new ArrayList<>(MAX_SHORTCUTS);
+        List<ShortcutInfo> filteredShortcuts = new ArrayList<>(MAX_SHORTCUTS);
         int numDynamic = 0;
         int size = shortcuts.size();
         for (int i = 0; i < size; i++) {
-            ShortcutInfoCompat shortcut = shortcuts.get(i);
+            ShortcutInfo shortcut = shortcuts.get(i);
             int filteredSize = filteredShortcuts.size();
             if (filteredSize < MAX_SHORTCUTS) {
                 // Always add the first MAX_SHORTCUTS to the filtered list.
@@ -123,7 +124,7 @@
 
     public static Runnable createUpdateRunnable(final Launcher launcher, final ItemInfo originalInfo,
             final Handler uiHandler, final PopupContainerWithArrow container,
-            final List<String> shortcutIds, final List<DeepShortcutView> shortcutViews,
+            final List<DeepShortcutView> shortcutViews,
             final List<NotificationKeyData> notificationKeys) {
         final ComponentName activity = originalInfo.getTargetComponent();
         final UserHandle user = originalInfo.user;
@@ -139,17 +140,17 @@
                 uiHandler.post(() -> container.applyNotificationInfos(infos));
             }
 
-            List<ShortcutInfoCompat> shortcuts = DeepShortcutManager.getInstance(launcher)
-                    .queryForShortcutsContainer(activity, shortcutIds, user);
+            List<ShortcutInfo> shortcuts = DeepShortcutManager.getInstance(launcher)
+                    .queryForShortcutsContainer(activity, user);
             String shortcutIdToDeDupe = notificationKeys.isEmpty() ? null
                     : notificationKeys.get(0).shortcutId;
             shortcuts = PopupPopulator.sortAndFilterShortcuts(shortcuts, shortcutIdToDeDupe);
             for (int i = 0; i < shortcuts.size() && i < shortcutViews.size(); i++) {
-                final ShortcutInfoCompat shortcut = shortcuts.get(i);
-                final ShortcutInfo si = new ShortcutInfo(shortcut, launcher);
+                final ShortcutInfo shortcut = shortcuts.get(i);
+                final WorkspaceItemInfo si = new WorkspaceItemInfo(shortcut, launcher);
                 // Use unbadged icon for the menu.
                 LauncherIcons li = LauncherIcons.obtain(launcher);
-                li.createShortcutIcon(shortcut, false /* badged */).applyTo(si);
+                si.applyFrom(li.createShortcutIcon(shortcut, false /* badged */));
                 li.recycle();
                 si.rank = i;
 
diff --git a/src/com/android/launcher3/popup/RemoteActionShortcut.java b/src/com/android/launcher3/popup/RemoteActionShortcut.java
new file mode 100644
index 0000000..41ab4df
--- /dev/null
+++ b/src/com/android/launcher3/popup/RemoteActionShortcut.java
@@ -0,0 +1,89 @@
+/*
+ * 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.popup;
+
+import android.app.PendingIntent;
+import android.app.RemoteAction;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.view.View;
+import android.widget.Toast;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.R;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+
+public class RemoteActionShortcut extends SystemShortcut<BaseDraggingActivity> {
+    private static final String TAG = "RemoteActionShortcut";
+    private static final boolean DEBUG = false;
+
+    private final RemoteAction mAction;
+
+    public RemoteActionShortcut(RemoteAction action) {
+        super(action.getIcon(), action.getTitle(), action.getContentDescription(),
+                R.id.action_remote_action_shortcut);
+        mAction = action;
+    }
+
+    @Override
+    public View.OnClickListener getOnClickListener(
+            final BaseDraggingActivity activity, final ItemInfo itemInfo) {
+        return view -> {
+            AbstractFloatingView.closeAllOpenViews(activity);
+
+            final String actionIdentity = mAction.getTitle() + ", " +
+                    itemInfo.getTargetComponent().getPackageName();
+            try {
+                if (DEBUG) Log.d(TAG, "Sending action: " + actionIdentity);
+                mAction.getActionIntent().send(
+                        activity,
+                        0,
+                        new Intent().putExtra(
+                                Intent.EXTRA_PACKAGE_NAME,
+                                itemInfo.getTargetComponent().getPackageName()),
+                        (pendingIntent, intent, resultCode, resultData, resultExtras) -> {
+                            if (DEBUG) Log.d(TAG, "Action is complete: " + actionIdentity);
+                            if (resultData != null && !resultData.isEmpty()) {
+                                Log.e(TAG, "Remote action returned result: " + actionIdentity
+                                        + " : " + resultData);
+                                Toast.makeText(activity, resultData, Toast.LENGTH_SHORT).show();
+                            }
+                        },
+                        new Handler(Looper.getMainLooper()));
+            } catch (PendingIntent.CanceledException e) {
+                Log.e(TAG, "Remote action canceled: " + actionIdentity, e);
+                Toast.makeText(activity, activity.getString(
+                        R.string.remote_action_failed,
+                        mAction.getTitle()),
+                        Toast.LENGTH_SHORT)
+                        .show();
+            }
+
+            activity.getUserEventDispatcher().logActionOnControl(LauncherLogProto.Action.Touch.TAP,
+                    LauncherLogProto.ControlType.REMOTE_ACTION_SHORTCUT, view);
+        };
+    }
+
+    @Override
+    public boolean isLeftGroup() {
+        return true;
+    }
+}
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index 693e532..563f3b3 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -3,17 +3,25 @@
 import static com.android.launcher3.userevent.nano.LauncherLogProto.Action;
 import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
 
+import android.app.ActivityOptions;
+import android.content.Context;
 import android.content.Intent;
 import android.graphics.Rect;
+import android.graphics.drawable.Icon;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
 import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.ImageView;
+import android.widget.TextView;
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.util.InstantAppResolver;
 import com.android.launcher3.util.PackageManagerHelper;
@@ -23,18 +31,93 @@
 import java.util.List;
 
 /**
- * Represents a system shortcut for a given app. The shortcut should have a static label and
- * icon, and an onClickListener that depends on the item that the shortcut services.
+ * Represents a system shortcut for a given app. The shortcut should have a label and icon, and an
+ * onClickListener that depends on the item that the shortcut services.
  *
  * Example system shortcuts, defined as inner classes, include Widgets and AppInfo.
  */
 public abstract class SystemShortcut<T extends BaseDraggingActivity> extends ItemInfo {
-    public final int iconResId;
-    public final int labelResId;
+    private final int mIconResId;
+    private final int mLabelResId;
+    private final Icon mIcon;
+    private final CharSequence mLabel;
+    private final CharSequence mContentDescription;
+    private final int mAccessibilityActionId;
 
     public SystemShortcut(int iconResId, int labelResId) {
-        this.iconResId = iconResId;
-        this.labelResId = labelResId;
+        mIconResId = iconResId;
+        mLabelResId = labelResId;
+        mAccessibilityActionId = labelResId;
+        mIcon = null;
+        mLabel = null;
+        mContentDescription = null;
+    }
+
+    public SystemShortcut(Icon icon, CharSequence label, CharSequence contentDescription,
+            int accessibilityActionId) {
+        mIcon = icon;
+        mLabel = label;
+        mContentDescription = contentDescription;
+        mAccessibilityActionId = accessibilityActionId;
+        mIconResId = 0;
+        mLabelResId = 0;
+    }
+
+    public SystemShortcut(SystemShortcut other) {
+        mIconResId = other.mIconResId;
+        mLabelResId = other.mLabelResId;
+        mIcon = other.mIcon;
+        mLabel = other.mLabel;
+        mContentDescription = other.mContentDescription;
+        mAccessibilityActionId = other.mAccessibilityActionId;
+    }
+
+    /**
+     * Should be in the left group of icons in app's context menu header.
+     */
+    public boolean isLeftGroup() {
+        return false;
+    }
+
+    public void setIconAndLabelFor(View iconView, TextView labelView) {
+        if (mIcon != null) {
+            mIcon.loadDrawableAsync(iconView.getContext(),
+                    iconView::setBackground,
+                    new Handler(Looper.getMainLooper()));
+        } else {
+            iconView.setBackgroundResource(mIconResId);
+        }
+
+        if (mLabel != null) {
+            labelView.setText(mLabel);
+        } else {
+            labelView.setText(mLabelResId);
+        }
+    }
+
+    public void setIconAndContentDescriptionFor(ImageView view) {
+        if (mIcon != null) {
+            mIcon.loadDrawableAsync(view.getContext(),
+                    view::setImageDrawable,
+                    new Handler(Looper.getMainLooper()));
+        } else {
+            view.setImageResource(mIconResId);
+        }
+
+        view.setContentDescription(getContentDescription(view.getContext()));
+    }
+
+    private CharSequence getContentDescription(Context context) {
+        return mContentDescription != null ? mContentDescription : context.getText(mLabelResId);
+    }
+
+    public AccessibilityNodeInfo.AccessibilityAction createAccessibilityAction(Context context) {
+        return new AccessibilityNodeInfo.AccessibilityAction(mAccessibilityActionId,
+                getContentDescription(context));
+    }
+
+    public boolean hasHandlerForAction(int action) {
+        return mAccessibilityActionId == action;
     }
 
     public abstract View.OnClickListener getOnClickListener(T activity, ItemInfo itemInfo);
@@ -77,9 +160,8 @@
             return (view) -> {
                 dismissTaskMenuView(activity);
                 Rect sourceBounds = activity.getViewBounds(view);
-                Bundle opts = activity.getActivityLaunchOptionsAsBundle(view);
                 new PackageManagerHelper(activity).startDetailsActivityForInfo(
-                        itemInfo, sourceBounds, opts);
+                        itemInfo, sourceBounds, ActivityOptions.makeBasic().toBundle());
                 activity.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
                         ControlType.APPINFO_TARGET, view);
             };
@@ -94,8 +176,8 @@
         @Override
         public View.OnClickListener getOnClickListener(
                 BaseDraggingActivity activity, ItemInfo itemInfo) {
-            boolean supportsWebUI = (itemInfo instanceof ShortcutInfo) &&
-                    ((ShortcutInfo) itemInfo).hasStatusFlag(ShortcutInfo.FLAG_SUPPORTS_WEB_UI);
+            boolean supportsWebUI = (itemInfo instanceof WorkspaceItemInfo) &&
+                    ((WorkspaceItemInfo) itemInfo).hasStatusFlag(WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI);
             boolean isInstantApp = false;
             if (itemInfo instanceof com.android.launcher3.AppInfo) {
                 com.android.launcher3.AppInfo appInfo = (com.android.launcher3.AppInfo) itemInfo;
@@ -113,7 +195,7 @@
             return view -> {
                 Intent intent = new PackageManagerHelper(view.getContext()).getMarketIntent(
                         itemInfo.getTargetComponent().getPackageName());
-                activity.startActivitySafely(view, intent, itemInfo);
+                activity.startActivitySafely(view, intent, itemInfo, null);
                 AbstractFloatingView.closeAllOpenViews(activity);
             };
         }
diff --git a/src/com/android/launcher3/popup/SystemShortcutFactory.java b/src/com/android/launcher3/popup/SystemShortcutFactory.java
new file mode 100644
index 0000000..516fafa
--- /dev/null
+++ b/src/com/android/launcher3/popup/SystemShortcutFactory.java
@@ -0,0 +1,57 @@
+/*
+ * 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.popup;
+
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.ResourceBasedOverride;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import androidx.annotation.NonNull;
+
+public class SystemShortcutFactory implements ResourceBasedOverride {
+
+    public static final MainThreadInitializedObject<SystemShortcutFactory> INSTANCE =
+            new MainThreadInitializedObject<>(c -> Overrides.getObject(
+                    SystemShortcutFactory.class, c, R.string.system_shortcut_factory_class));
+
+    /** Note that these are in order of priority. */
+    private final SystemShortcut[] mAllShortcuts;
+
+    @SuppressWarnings("unused")
+    public SystemShortcutFactory() {
+        this(new SystemShortcut.AppInfo(),
+                new SystemShortcut.Widgets(), new SystemShortcut.Install());
+    }
+
+    protected SystemShortcutFactory(SystemShortcut... shortcuts) {
+        mAllShortcuts = shortcuts;
+    }
+
+    public @NonNull List<SystemShortcut> getEnabledShortcuts(Launcher launcher, ItemInfo info) {
+        List<SystemShortcut> systemShortcuts = new ArrayList<>();
+        for (SystemShortcut systemShortcut : mAllShortcuts) {
+            if (systemShortcut.getOnClickListener(launcher, info) != null) {
+                systemShortcuts.add(systemShortcut);
+            }
+        }
+        return systemShortcuts;
+    }
+}
diff --git a/src/com/android/launcher3/provider/ImportDataTask.java b/src/com/android/launcher3/provider/ImportDataTask.java
index b1dd003..7b62f53 100644
--- a/src/com/android/launcher3/provider/ImportDataTask.java
+++ b/src/com/android/launcher3/provider/ImportDataTask.java
@@ -32,8 +32,8 @@
 import android.os.Process;
 import android.text.TextUtils;
 import android.util.ArrayMap;
-import android.util.LongSparseArray;
 import android.util.SparseBooleanArray;
+
 import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback;
 import com.android.launcher3.DefaultLayoutParser;
 import com.android.launcher3.LauncherAppState;
@@ -42,15 +42,15 @@
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.LauncherSettings.Settings;
-import com.android.launcher3.LauncherSettings.WorkspaceScreens;
-import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.model.GridSizeMigrationTask;
-import com.android.launcher3.util.LongArrayMap;
+import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.IntSparseArrayMap;
+
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -70,7 +70,6 @@
 
     private final Context mContext;
 
-    private final Uri mOtherScreensUri;
     private final Uri mOtherFavoritesUri;
 
     private int mHotseatSize;
@@ -79,41 +78,14 @@
 
     private ImportDataTask(Context context, String sourceAuthority) {
         mContext = context;
-        mOtherScreensUri = Uri.parse("content://" +
-                sourceAuthority + "/" + WorkspaceScreens.TABLE_NAME);
         mOtherFavoritesUri = Uri.parse("content://" + sourceAuthority + "/" + Favorites.TABLE_NAME);
     }
 
     public boolean importWorkspace() throws Exception {
-        ArrayList<Long> allScreens = LauncherDbUtils.getScreenIdsFromCursor(
-                mContext.getContentResolver().query(mOtherScreensUri, null, null, null,
-                        LauncherSettings.WorkspaceScreens.SCREEN_RANK));
         FileLog.d(TAG, "Importing DB from " + mOtherFavoritesUri);
 
-        // During import we reset the screen IDs to 0-indexed values.
-        if (allScreens.isEmpty()) {
-            // No thing to migrate
-            FileLog.e(TAG, "No data found to import");
-            return false;
-        }
-
         mHotseatSize = mMaxGridSizeX = mMaxGridSizeY = 0;
-
-        // Build screen update
-        ArrayList<ContentProviderOperation> screenOps = new ArrayList<>();
-        int count = allScreens.size();
-        LongSparseArray<Long> screenIdMap = new LongSparseArray<>(count);
-        for (int i = 0; i < count; i++) {
-            ContentValues v = new ContentValues();
-            v.put(LauncherSettings.WorkspaceScreens._ID, i);
-            v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
-            screenIdMap.put(allScreens.get(i), (long) i);
-            screenOps.add(ContentProviderOperation.newInsert(
-                    LauncherSettings.WorkspaceScreens.CONTENT_URI).withValues(v).build());
-        }
-        mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY, screenOps);
-        importWorkspaceItems(allScreens.get(0), screenIdMap);
-
+        importWorkspaceItems();
         GridSizeMigrationTask.markForMigration(mContext, mMaxGridSizeX, mMaxGridSizeY, mHotseatSize);
 
         // Create empty DB flag.
@@ -127,17 +99,17 @@
      * 2) For home screen entries, maps the screen id based on {@param screenIdMap}
      * 3) In the end fills any holes in hotseat with items from default hotseat layout.
      */
-    private void importWorkspaceItems(
-            long firsetScreenId, LongSparseArray<Long> screenIdMap) throws Exception {
+    private void importWorkspaceItems() throws Exception {
         String profileId = Long.toString(UserManagerCompat.getInstance(mContext)
                 .getSerialNumberForUser(Process.myUserHandle()));
 
         boolean createEmptyRowOnFirstScreen;
         if (FeatureFlags.QSB_ON_FIRST_SCREEN) {
             try (Cursor c = mContext.getContentResolver().query(mOtherFavoritesUri, null,
-                    // get items on the first row of the first screen
-                    "profileId = ? AND container = -100 AND screen = ? AND cellY = 0",
-                    new String[]{profileId, Long.toString(firsetScreenId)},
+                    // get items on the first row of the first screen (min screen id)
+                    "profileId = ? AND container = -100 AND cellY = 0 AND screen = " +
+                    "(SELECT MIN(screen) FROM favorites WHERE container = -100)",
+                    new String[]{profileId},
                     null)) {
                 // First row of first screen is not empty
                 createEmptyRowOnFirstScreen = c.moveToNext();
@@ -161,7 +133,7 @@
                         Favorites.PROFILE_ID + " = ?", new String[]{profileId},
                         // Get the items sorted by container, so that the folders are loaded
                         // before the corresponding items.
-                        Favorites.CONTAINER)) {
+                        Favorites.CONTAINER + " , " + Favorites.SCREEN)) {
 
             // various columns we expect to exist.
             final int idIndex = c.getColumnIndexOrThrow(Favorites._ID);
@@ -183,6 +155,7 @@
             SparseBooleanArray mValidFolders = new SparseBooleanArray();
             ContentValues values = new ContentValues();
 
+            Integer firstScreenId = null;
             while (c.moveToNext()) {
                 values.clear();
                 int id = c.getInt(idIndex);
@@ -190,7 +163,7 @@
                 int type = c.getInt(itemTypeIndex);
                 int container = c.getInt(containerIndex);
 
-                long screen = c.getLong(screenIndex);
+                int screen = c.getInt(screenIndex);
 
                 int cellX = c.getInt(cellXIndex);
                 int cellY = c.getInt(cellYIndex);
@@ -199,16 +172,21 @@
 
                 switch (container) {
                     case Favorites.CONTAINER_DESKTOP: {
-                        Long newScreenId = screenIdMap.get(screen);
-                        if (newScreenId == null) {
-                            FileLog.d(TAG, String.format("Skipping item %d, type %d not on a valid screen %d", id, type, screen));
+                        if (screen < Workspace.FIRST_SCREEN_ID) {
+                            FileLog.d(TAG, String.format(
+                                    "Skipping item %d, type %d not on a valid screen %d",
+                                    id, type, screen));
                             continue;
                         }
+                        if (firstScreenId == null) {
+                            firstScreenId = screen;
+                        }
                         // Reset the screen to 0-index value
-                        screen = newScreenId;
-                        if (createEmptyRowOnFirstScreen && screen == Workspace.FIRST_SCREEN_ID) {
+                        if (createEmptyRowOnFirstScreen && firstScreenId.equals(screen)) {
                             // Shift items by 1.
                             cellY++;
+                            // Change the screen id to first screen
+                            screen = Workspace.FIRST_SCREEN_ID;
                         }
 
                         mMaxGridSizeX = Math.max(mMaxGridSizeX, cellX + spanX);
@@ -216,7 +194,7 @@
                         break;
                     }
                     case Favorites.CONTAINER_HOTSEAT: {
-                        mHotseatSize = Math.max(mHotseatSize, (int) screen + 1);
+                        mHotseatSize = Math.max(mHotseatSize, screen + 1);
                         break;
                     }
                     default:
@@ -306,18 +284,15 @@
             insertOperations.clear();
         }
 
-        LongArrayMap<Object> hotseatItems = GridSizeMigrationTask.removeBrokenHotseatItems(mContext);
+        IntSparseArrayMap<Object> hotseatItems = GridSizeMigrationTask.removeBrokenHotseatItems(mContext);
         int myHotseatCount = LauncherAppState.getIDP(mContext).numHotseatIcons;
-        if (!FeatureFlags.NO_ALL_APPS_ICON) {
-            myHotseatCount--;
-        }
         if (hotseatItems.size() < myHotseatCount) {
             // Insufficient hotseat items. Add a few more.
             HotseatParserCallback parserCallback = new HotseatParserCallback(
                     hotseatTargetApps, hotseatItems, insertOperations, maxId + 1, myHotseatCount);
             new HotseatLayoutParser(mContext,
-                    parserCallback).loadLayout(null, new ArrayList<Long>());
-            mHotseatSize = (int) hotseatItems.keyAt(hotseatItems.size() - 1) + 1;
+                    parserCallback).loadLayout(null, new IntArray());
+            mHotseatSize = hotseatItems.keyAt(hotseatItems.size() - 1) + 1;
 
             if (!insertOperations.isEmpty()) {
                 mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY,
@@ -378,18 +353,13 @@
         return false;
     }
 
-    private static int getMyHotseatLayoutId(Context context) {
-        return LauncherAppState.getIDP(context).numHotseatIcons <= 5
-                ? R.xml.dw_phone_hotseat
-                : R.xml.dw_tablet_hotseat;
-    }
-
     /**
      * Extension of {@link DefaultLayoutParser} which only allows icons and shortcuts.
      */
     private static class HotseatLayoutParser extends DefaultLayoutParser {
         public HotseatLayoutParser(Context context, LayoutParserCallback callback) {
-            super(context, null, callback, context.getResources(), getMyHotseatLayoutId(context));
+            super(context, null, callback, context.getResources(),
+                    LauncherAppState.getIDP(context).defaultLayoutId);
         }
 
         @Override
@@ -408,13 +378,13 @@
      */
     private static class HotseatParserCallback implements LayoutParserCallback {
         private final HashSet<String> mExistingApps;
-        private final LongArrayMap<Object> mExistingItems;
+        private final IntSparseArrayMap<Object> mExistingItems;
         private final ArrayList<ContentProviderOperation> mOutOps;
         private final int mRequiredSize;
         private int mStartItemId;
 
         HotseatParserCallback(
-                HashSet<String> existingApps, LongArrayMap<Object> existingItems,
+                HashSet<String> existingApps, IntSparseArrayMap<Object> existingItems,
                 ArrayList<ContentProviderOperation> outOps, int startItemId, int requiredSize) {
             mExistingApps = existingApps;
             mExistingItems = existingItems;
@@ -424,16 +394,22 @@
         }
 
         @Override
-        public long generateNewItemId() {
+        public int generateNewItemId() {
             return mStartItemId++;
         }
 
         @Override
-        public long insertAndCheck(SQLiteDatabase db, ContentValues values) {
+        public int insertAndCheck(SQLiteDatabase db, ContentValues values) {
             if (mExistingItems.size() >= mRequiredSize) {
                 // No need to add more items.
                 return 0;
             }
+            if (!Integer.valueOf(Favorites.CONTAINER_HOTSEAT)
+                    .equals(values.getAsInteger(Favorites.CONTAINER))) {
+                // Ignore items which are not for hotseat.
+                return 0;
+            }
+
             Intent intent;
             try {
                 intent = Intent.parseUri(values.getAsString(Favorites.INTENT), 0);
@@ -448,7 +424,7 @@
             mExistingApps.add(pkg);
 
             // find next vacant spot.
-            long screen = 0;
+            int screen = 0;
             while (mExistingItems.get(screen) != null) {
                 screen++;
             }
diff --git a/src/com/android/launcher3/provider/LauncherDbUtils.java b/src/com/android/launcher3/provider/LauncherDbUtils.java
index 74373d3..2c843f9 100644
--- a/src/com/android/launcher3/provider/LauncherDbUtils.java
+++ b/src/com/android/launcher3/provider/LauncherDbUtils.java
@@ -21,14 +21,14 @@
 import android.database.Cursor;
 import android.database.DatabaseUtils;
 import android.database.sqlite.SQLiteDatabase;
+import android.os.Binder;
 import android.util.Log;
 
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.LauncherSettings.WorkspaceScreens;
+import com.android.launcher3.util.IntArray;
 
-import java.util.ArrayList;
-import java.util.Collection;
+import java.util.Locale;
 
 /**
  * A set of utility methods for Launcher DB used for DB updates and migration.
@@ -46,26 +46,25 @@
      */
     public static boolean prepareScreenZeroToHostQsb(Context context, SQLiteDatabase db) {
         try (SQLiteTransaction t = new SQLiteTransaction(db)) {
-            // Get the existing screens
-            ArrayList<Long> screenIds = getScreenIdsFromCursor(db.query(WorkspaceScreens.TABLE_NAME,
-                    null, null, null, null, null, WorkspaceScreens.SCREEN_RANK));
+            // Get the first screen
+            final int firstScreenId;
+            try (Cursor c = db.rawQuery(String.format(Locale.ENGLISH,
+                    "SELECT MIN(%1$s) from %2$s where %3$s = %4$d",
+                    Favorites.SCREEN, Favorites.TABLE_NAME, Favorites.CONTAINER,
+                    Favorites.CONTAINER_DESKTOP), null)) {
 
-            if (screenIds.isEmpty()) {
-                // No update needed
-                t.commit();
-                return true;
-            }
-            if (screenIds.get(0) != 0) {
-                // First screen is not 0, we need to rename screens
-                if (screenIds.indexOf(0L) > -1) {
-                    // There is already a screen 0. First rename it to a different screen.
-                    long newScreenId = 1;
-                    while (screenIds.indexOf(newScreenId) > -1) newScreenId++;
-                    renameScreen(db, 0, newScreenId);
+                if (!c.moveToNext()) {
+                    // No update needed
+                    t.commit();
+                    return true;
                 }
 
+                firstScreenId = c.getInt(0);
+            }
+
+            if (firstScreenId != 0) {
                 // Rename the first screen to 0.
-                renameScreen(db, screenIds.get(0), 0);
+                renameScreen(db, firstScreenId, 0);
             }
 
             // Check if the first row is empty
@@ -86,42 +85,41 @@
         }
     }
 
-    private static void renameScreen(SQLiteDatabase db, long oldScreen, long newScreen) {
-        String[] whereParams = new String[] { Long.toString(oldScreen) };
-
+    private static void renameScreen(SQLiteDatabase db, int oldScreen, int newScreen) {
+        String[] whereParams = new String[] { Integer.toString(oldScreen) };
         ContentValues values = new ContentValues();
-        values.put(WorkspaceScreens._ID, newScreen);
-        db.update(WorkspaceScreens.TABLE_NAME, values, "_id = ?", whereParams);
-
-        values.clear();
         values.put(Favorites.SCREEN, newScreen);
         db.update(Favorites.TABLE_NAME, values, "container = -100 and screen = ?", whereParams);
     }
 
-    /**
-     * Parses the cursor containing workspace screens table and returns the list of screen IDs
-     */
-    public static ArrayList<Long> getScreenIdsFromCursor(Cursor sc) {
-        try {
-            return iterateCursor(sc,
-                    sc.getColumnIndexOrThrow(WorkspaceScreens._ID),
-                    new ArrayList<Long>());
-        } finally {
-            sc.close();
+    public static IntArray queryIntArray(SQLiteDatabase db, String tableName, String columnName,
+            String selection, String groupBy, String orderBy) {
+        IntArray out = new IntArray();
+        try (Cursor c = db.query(tableName, new String[] { columnName }, selection, null,
+                groupBy, null, orderBy)) {
+            while (c.moveToNext()) {
+                out.add(c.getInt(0));
+            }
+        }
+        return out;
+    }
+
+    public static boolean tableExists(SQLiteDatabase db, String tableName) {
+        try (Cursor c = db.query(true, "sqlite_master", new String[] {"tbl_name"},
+                "tbl_name = ?", new String[] {tableName},
+                null, null, null, null, null)) {
+            return c.getCount() > 0;
         }
     }
 
-    public static <T extends Collection<Long>> T iterateCursor(Cursor c, int columnIndex, T out) {
-        while (c.moveToNext()) {
-            out.add(c.getLong(columnIndex));
-        }
-        return out;
+    public static void dropTable(SQLiteDatabase db, String tableName) {
+        db.execSQL("DROP TABLE IF EXISTS " + tableName);
     }
 
     /**
      * Utility class to simplify managing sqlite transactions
      */
-    public static class SQLiteTransaction implements AutoCloseable {
+    public static class SQLiteTransaction extends Binder implements AutoCloseable {
         private final SQLiteDatabase mDb;
 
         public SQLiteTransaction(SQLiteDatabase db) {
@@ -137,5 +135,9 @@
         public void close() {
             mDb.endTransaction();
         }
+
+        public SQLiteDatabase getDb() {
+            return mDb;
+        }
     }
 }
diff --git a/src/com/android/launcher3/provider/LossyScreenMigrationTask.java b/src/com/android/launcher3/provider/LossyScreenMigrationTask.java
index 51890d1..6d839f3 100644
--- a/src/com/android/launcher3/provider/LossyScreenMigrationTask.java
+++ b/src/com/android/launcher3/provider/LossyScreenMigrationTask.java
@@ -27,7 +27,7 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.model.GridSizeMigrationTask;
-import com.android.launcher3.util.LongArrayMap;
+import com.android.launcher3.util.IntSparseArrayMap;
 
 import java.util.ArrayList;
 
@@ -37,26 +37,18 @@
  */
 public class LossyScreenMigrationTask extends GridSizeMigrationTask {
 
-    private final SQLiteDatabase mDb;
-
-    private final LongArrayMap<DbEntry> mOriginalItems;
-    private final LongArrayMap<DbEntry> mUpdates;
+    private final IntSparseArrayMap<DbEntry> mOriginalItems;
+    private final IntSparseArrayMap<DbEntry> mUpdates;
 
     protected LossyScreenMigrationTask(
             Context context, InvariantDeviceProfile idp, SQLiteDatabase db) {
         // Decrease the rows count by 1
-        super(context, idp, getValidPackages(context),
+        super(context, db, getValidPackages(context),
                 new Point(idp.numColumns, idp.numRows + 1),
                 new Point(idp.numColumns, idp.numRows));
 
-        mDb = db;
-        mOriginalItems = new LongArrayMap<>();
-        mUpdates = new LongArrayMap<>();
-    }
-
-    @Override
-    protected Cursor queryWorkspace(String[] columns, String where) {
-        return mDb.query(Favorites.TABLE_NAME, columns, where, null, null, null, null);
+        mOriginalItems = new IntSparseArrayMap<>();
+        mUpdates = new IntSparseArrayMap<>();
     }
 
     @Override
@@ -65,7 +57,7 @@
     }
 
     @Override
-    protected ArrayList<DbEntry> loadWorkspaceEntries(long screen) {
+    protected ArrayList<DbEntry> loadWorkspaceEntries(int screen) {
         ArrayList<DbEntry> result = super.loadWorkspaceEntries(screen);
         for (DbEntry entry : result) {
             mOriginalItems.put(entry.id, entry.copy());
@@ -90,7 +82,7 @@
                 tempValues.clear();
                 update.addToContentValues(tempValues);
                 mDb.update(Favorites.TABLE_NAME, tempValues, "_id = ?",
-                        new String[] {Long.toString(update.id)});
+                        new String[] {Integer.toString(update.id)});
             }
         }
 
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index 5230160..3c0c5fd 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -16,15 +16,27 @@
 
 package com.android.launcher3.provider;
 
+import static com.android.launcher3.Utilities.getIntArrayFromString;
+import static com.android.launcher3.Utilities.getStringFromIntArray;
+import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
+
+import android.app.backup.BackupManager;
 import android.content.ContentValues;
 import android.content.Context;
+import android.content.SharedPreferences;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
+import android.os.UserHandle;
+import android.util.LongSparseArray;
+import android.util.SparseLongArray;
 
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.AppWidgetsRestoredReceiver;
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherProvider.DatabaseHelper;
 import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
@@ -46,10 +58,16 @@
     private static final String INFO_COLUMN_NAME = "name";
     private static final String INFO_COLUMN_DEFAULT_VALUE = "dflt_value";
 
-    public static boolean performRestore(DatabaseHelper helper) {
+    private static final String APPWIDGET_OLD_IDS = "appwidget_old_ids";
+    private static final String APPWIDGET_IDS = "appwidget_ids";
+
+    public static boolean performRestore(Context context, DatabaseHelper helper,
+            BackupManager backupManager) {
         SQLiteDatabase db = helper.getWritableDatabase();
         try (SQLiteTransaction t = new SQLiteTransaction(db)) {
-            new RestoreDbTask().sanitizeDB(helper, db);
+            RestoreDbTask task = new RestoreDbTask();
+            task.sanitizeDB(helper, db, backupManager);
+            task.restoreAppWidgetIdsIfExists(context);
             t.commit();
             return true;
         } catch (Exception e) {
@@ -60,30 +78,54 @@
 
     /**
      * Makes the following changes in the provider DB.
-     *   1. Removes all entries belonging to a managed profile as managed profiles
-     *      cannot be restored.
+     *   1. Removes all entries belonging to any profiles that were not restored.
      *   2. Marks all entries as restored. The flags are updated during first load or as
      *      the restored apps get installed.
-     *   3. If the user serial for primary profile is different than that of the previous device,
-     *      update the entries to the new profile id.
+     *   3. If the user serial for any restored profile is different than that of the previous
+     *      device, update the entries to the new profile id.
      */
-    private void sanitizeDB(DatabaseHelper helper, SQLiteDatabase db) throws Exception {
+    private void sanitizeDB(DatabaseHelper helper, SQLiteDatabase db, BackupManager backupManager)
+            throws Exception {
+        // Primary user ids
+        long myProfileId = helper.getDefaultUserSerial();
         long oldProfileId = getDefaultProfileId(db);
-        // Delete all entries which do not belong to the main user
-        int itemsDeleted = db.delete(
-                Favorites.TABLE_NAME, "profileId != ?", new String[]{Long.toString(oldProfileId)});
+        LongSparseArray<Long> oldManagedProfileIds = getManagedProfileIds(db, oldProfileId);
+        LongSparseArray<Long> profileMapping = new LongSparseArray<>(oldManagedProfileIds.size()
+                + 1);
+
+        // Build mapping of restored profile ids to their new profile ids.
+        profileMapping.put(oldProfileId, myProfileId);
+        for (int i = oldManagedProfileIds.size() - 1; i >= 0; --i) {
+            long oldManagedProfileId = oldManagedProfileIds.keyAt(i);
+            UserHandle user = getUserForAncestralSerialNumber(backupManager, oldManagedProfileId);
+            if (user != null) {
+                long newManagedProfileId = helper.getSerialNumberForUser(user);
+                profileMapping.put(oldManagedProfileId, newManagedProfileId);
+            }
+        }
+
+        // Delete all entries which do not belong to any restored profile(s).
+        int numProfiles = profileMapping.size();
+        String[] profileIds = new String[numProfiles];
+        profileIds[0] = Long.toString(oldProfileId);
+        StringBuilder whereClause = new StringBuilder("profileId != ?");
+        for (int i = profileMapping.size() - 1; i >= 1; --i) {
+            whereClause.append(" AND profileId != ?");
+            profileIds[i] = Long.toString(profileMapping.keyAt(i));
+        }
+        int itemsDeleted = db.delete(Favorites.TABLE_NAME, whereClause.toString(), profileIds);
         if (itemsDeleted > 0) {
-            FileLog.d(TAG, itemsDeleted + " items belonging to a managed profile, were deleted");
+            FileLog.d(TAG, itemsDeleted + " items from unrestored user(s) were deleted");
         }
 
         // Mark all items as restored.
         boolean keepAllIcons = Utilities.isPropertyEnabled(LogConfig.KEEP_ALL_ICONS);
         ContentValues values = new ContentValues();
-        values.put(Favorites.RESTORED, ShortcutInfo.FLAG_RESTORED_ICON
-                | (keepAllIcons ? ShortcutInfo.FLAG_RESTORE_STARTED : 0));
+        values.put(Favorites.RESTORED, WorkspaceItemInfo.FLAG_RESTORED_ICON
+                | (keepAllIcons ? WorkspaceItemInfo.FLAG_RESTORE_STARTED : 0));
         db.update(Favorites.TABLE_NAME, values, null, null);
 
-        // Mark widgets with appropriate restore flag
+        // Mark widgets with appropriate restore flag.
         values.put(Favorites.RESTORED,  LauncherAppWidgetInfo.FLAG_ID_NOT_VALID |
                 LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY |
                 LauncherAppWidgetInfo.FLAG_UI_NOT_READY |
@@ -91,27 +133,89 @@
         db.update(Favorites.TABLE_NAME, values, "itemType = ?",
                 new String[]{Integer.toString(Favorites.ITEM_TYPE_APPWIDGET)});
 
-        long myProfileId = helper.getDefaultUserSerial();
-        if (Utilities.longCompare(oldProfileId, myProfileId) != 0) {
-            FileLog.d(TAG, "Changing primary user id from " + oldProfileId + " to " + myProfileId);
-            migrateProfileId(db, myProfileId);
+        // Migrate ids. To avoid any overlap, we initially move conflicting ids to a temp location.
+        // Using Long.MIN_VALUE since profile ids can not be negative, so there will be no overlap.
+        final long tempLocationOffset = Long.MIN_VALUE;
+        SparseLongArray tempMigratedIds = new SparseLongArray(profileMapping.size());
+        int numTempMigrations = 0;
+        for (int i = profileMapping.size() - 1; i >= 0; --i) {
+            long oldId = profileMapping.keyAt(i);
+            long newId = profileMapping.valueAt(i);
+
+            if (oldId != newId) {
+                if (profileMapping.indexOfKey(newId) >= 0) {
+                    tempMigratedIds.put(numTempMigrations, newId);
+                    numTempMigrations++;
+                    newId = tempLocationOffset + newId;
+                }
+                migrateProfileId(db, oldId, newId);
+            }
+        }
+
+        // Migrate ids from their temporary id to their actual final id.
+        for (int i = tempMigratedIds.size() - 1; i >= 0; --i) {
+            long newId = tempMigratedIds.valueAt(i);
+            migrateProfileId(db, tempLocationOffset + newId, newId);
+        }
+
+        if (myProfileId != oldProfileId) {
+            changeDefaultColumn(db, myProfileId);
         }
     }
 
     /**
-     * Updates profile id of all entries and changes the default value for the column.
+     * Updates profile id of all entries from {@param oldProfileId} to {@param newProfileId}.
      */
-    protected void migrateProfileId(SQLiteDatabase db, long newProfileId) {
+    protected void migrateProfileId(SQLiteDatabase db, long oldProfileId, long newProfileId) {
+        FileLog.d(TAG, "Changing profile user id from " + oldProfileId + " to " + newProfileId);
         // Update existing entries.
         ContentValues values = new ContentValues();
         values.put(Favorites.PROFILE_ID, newProfileId);
-        db.update(Favorites.TABLE_NAME, values, null, null);
+        db.update(Favorites.TABLE_NAME, values, "profileId = ?",
+                new String[]{Long.toString(oldProfileId)});
 
         // Change default value of the column.
         db.execSQL("ALTER TABLE favorites RENAME TO favorites_old;");
         Favorites.addTableToDb(db, newProfileId, false);
         db.execSQL("INSERT INTO favorites SELECT * FROM favorites_old;");
-        db.execSQL("DROP TABLE favorites_old;");
+        dropTable(db, "favorites_old");
+    }
+
+
+    /**
+     * Changes the default value for the column.
+     */
+    protected void changeDefaultColumn(SQLiteDatabase db, long newProfileId) {
+        db.execSQL("ALTER TABLE favorites RENAME TO favorites_old;");
+        Favorites.addTableToDb(db, newProfileId, false);
+        db.execSQL("INSERT INTO favorites SELECT * FROM favorites_old;");
+        dropTable(db, "favorites_old");
+    }
+
+    /**
+     * Returns a list of the managed profile id(s) used in the favorites table of the provided db.
+     */
+    private LongSparseArray<Long> getManagedProfileIds(SQLiteDatabase db, long defaultProfileId) {
+        LongSparseArray<Long> ids = new LongSparseArray<>();
+        try (Cursor c = db.rawQuery("SELECT profileId from favorites WHERE profileId != ? "
+                + "GROUP BY profileId", new String[] {Long.toString(defaultProfileId)})){
+                while (c.moveToNext()) {
+                    ids.put(c.getLong(c.getColumnIndex(Favorites.PROFILE_ID)), null);
+                }
+        }
+        return ids;
+    }
+
+    /**
+     * Returns a UserHandle of a restored managed profile with the given serial number, or null
+     * if none found.
+     */
+    private UserHandle getUserForAncestralSerialNumber(BackupManager backupManager,
+            long ancestralSerialNumber) {
+        if (!Utilities.ATLEAST_Q) {
+            return null;
+        }
+        return backupManager.getUserForAncestralSerialNumber(ancestralSerialNumber);
     }
 
     /**
@@ -137,4 +241,27 @@
         FileLog.d(TAG, "Restore data received through full backup " + isPending);
         Utilities.getPrefs(context).edit().putBoolean(RESTORE_TASK_PENDING, isPending).commit();
     }
+
+    private void restoreAppWidgetIdsIfExists(Context context) {
+        SharedPreferences prefs = Utilities.getPrefs(context);
+        if (prefs.contains(APPWIDGET_OLD_IDS) && prefs.contains(APPWIDGET_IDS)) {
+            AppWidgetsRestoredReceiver.restoreAppWidgetIds(context,
+                    getIntArrayFromString(prefs.getString(APPWIDGET_OLD_IDS, "")),
+                    getIntArrayFromString(prefs.getString(APPWIDGET_IDS, "")));
+        } else {
+            FileLog.d(TAG, "No app widget ids to restore.");
+        }
+
+        prefs.edit().remove(APPWIDGET_OLD_IDS)
+                .remove(APPWIDGET_IDS).apply();
+    }
+
+    public static void setRestoredAppWidgetIds(Context context, @NonNull int[] oldIds,
+            @NonNull int[] newIds) {
+        Utilities.getPrefs(context).edit()
+                .putString(APPWIDGET_OLD_IDS, getStringFromIntArray(oldIds))
+                .putString(APPWIDGET_IDS, getStringFromIntArray(newIds))
+                .commit();
+    }
+
 }
diff --git a/src/com/android/launcher3/qsb/QsbContainerView.java b/src/com/android/launcher3/qsb/QsbContainerView.java
index 5b8ae58..857ea05 100644
--- a/src/com/android/launcher3/qsb/QsbContainerView.java
+++ b/src/com/android/launcher3/qsb/QsbContainerView.java
@@ -44,6 +44,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.graphics.FragmentWithPreview;
 
 /**
  * A frame layout which contains a QSB. This internally uses fragment to bind the view, which
@@ -78,7 +79,7 @@
     /**
      * A fragment to display the QSB.
      */
-    public static class QsbFragment extends Fragment {
+    public static class QsbFragment extends FragmentWithPreview {
 
         public static final int QSB_WIDGET_HOST_ID = 1026;
         private static final int REQUEST_BIND_QSB = 1;
@@ -93,14 +94,13 @@
         private int mOrientation;
 
         @Override
-        public void onCreate(Bundle savedInstanceState) {
-            super.onCreate(savedInstanceState);
+        public void onInit(Bundle savedInstanceState) {
             mQsbWidgetHost = createHost();
             mOrientation = getContext().getResources().getConfiguration().orientation;
         }
 
         protected QsbWidgetHost createHost() {
-            return new QsbWidgetHost(getActivity(), QSB_WIDGET_HOST_ID,
+            return new QsbWidgetHost(getContext(), QSB_WIDGET_HOST_ID,
                     (c) -> new QsbWidgetHostView(c));
         }
 
@@ -110,7 +110,7 @@
         public View onCreateView(
                 LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
 
-            mWrapper = new FrameLayout(getActivity());
+            mWrapper = new FrameLayout(getContext());
 
             // Only add the view when enabled
             if (isQsbEnabled()) {
@@ -126,16 +126,16 @@
                 return getDefaultView(container, false /* show setup icon */);
             }
             Bundle opts = createBindOptions();
-            Activity activity = getActivity();
-            AppWidgetManager widgetManager = AppWidgetManager.getInstance(activity);
+            Context context = getContext();
+            AppWidgetManager widgetManager = AppWidgetManager.getInstance(context);
 
-            int widgetId = Utilities.getPrefs(activity).getInt(mKeyWidgetId, -1);
+            int widgetId = Utilities.getPrefs(context).getInt(mKeyWidgetId, -1);
             AppWidgetProviderInfo widgetInfo = widgetManager.getAppWidgetInfo(widgetId);
             boolean isWidgetBound = (widgetInfo != null) &&
                     widgetInfo.provider.equals(mWidgetInfo.provider);
 
             int oldWidgetId = widgetId;
-            if (!isWidgetBound) {
+            if (!isWidgetBound && !isInPreviewMode()) {
                 if (widgetId > -1) {
                     // widgetId is already bound and its not the correct provider. reset host.
                     mQsbWidgetHost.deleteHost();
@@ -155,14 +155,16 @@
             }
 
             if (isWidgetBound) {
-                mQsb = (QsbWidgetHostView) mQsbWidgetHost.createView(activity, widgetId, mWidgetInfo);
+                mQsb = (QsbWidgetHostView) mQsbWidgetHost.createView(context, widgetId, mWidgetInfo);
                 mQsb.setId(R.id.qsb_widget);
 
-                if (!Utilities.containsAll(AppWidgetManager.getInstance(activity)
-                        .getAppWidgetOptions(widgetId), opts)) {
-                    mQsb.updateAppWidgetOptions(opts);
+                if (!isInPreviewMode()) {
+                    if (!containsAll(AppWidgetManager.getInstance(context)
+                            .getAppWidgetOptions(widgetId), opts)) {
+                        mQsb.updateAppWidgetOptions(opts);
+                    }
+                    mQsbWidgetHost.startListening();
                 }
-                mQsbWidgetHost.startListening();
                 return mQsb;
             }
 
@@ -171,7 +173,7 @@
         }
 
         private void saveWidgetId(int widgetId) {
-            Utilities.getPrefs(getActivity()).edit().putInt(mKeyWidgetId, widgetId).apply();
+            Utilities.getPrefs(getContext()).edit().putInt(mKeyWidgetId, widgetId).apply();
         }
 
         @Override
@@ -206,7 +208,7 @@
                 return;
             }
 
-            if (mWrapper != null && getActivity() != null) {
+            if (mWrapper != null && getContext() != null) {
                 mWrapper.removeAllViews();
                 mWrapper.addView(createQsb(mWrapper));
             }
@@ -217,10 +219,10 @@
         }
 
         protected Bundle createBindOptions() {
-            InvariantDeviceProfile idp = LauncherAppState.getIDP(getActivity());
+            InvariantDeviceProfile idp = LauncherAppState.getIDP(getContext());
 
             Bundle opts = new Bundle();
-            Rect size = AppWidgetResizeFrame.getWidgetSizeRanges(getActivity(),
+            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);
@@ -252,14 +254,14 @@
          */
         protected AppWidgetProviderInfo getSearchWidgetProvider() {
             SearchManager searchManager =
-                    (SearchManager) getActivity().getSystemService(Context.SEARCH_SERVICE);
+                    (SearchManager) getContext().getSystemService(Context.SEARCH_SERVICE);
             ComponentName searchComponent = searchManager.getGlobalSearchActivity();
             if (searchComponent == null) return null;
             String providerPkg = searchComponent.getPackageName();
 
             AppWidgetProviderInfo defaultWidgetForSearchPackage = null;
 
-            AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getActivity());
+            AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getContext());
             for (AppWidgetProviderInfo info : appWidgetManager.getInstalledProviders()) {
                 if (info.provider.getPackageName().equals(providerPkg) && info.configure == null) {
                     if ((info.widgetCategory
@@ -294,4 +296,24 @@
 
         QsbWidgetHostView newView(Context context);
     }
+
+    /**
+     * Returns true if {@param original} contains all entries defined in {@param updates} and
+     * have the same value.
+     * The comparison uses {@link Object#equals(Object)} to compare the values.
+     */
+    private static boolean containsAll(Bundle original, Bundle updates) {
+        for (String key : updates.keySet()) {
+            Object value1 = updates.get(key);
+            Object value2 = original.get(key);
+            if (value1 == null) {
+                if (value2 != null) {
+                    return false;
+                }
+            } else if (!value1.equals(value2)) {
+                return false;
+            }
+        }
+        return true;
+    }
 }
diff --git a/src/com/android/launcher3/qsb/QsbWidgetHostView.java b/src/com/android/launcher3/qsb/QsbWidgetHostView.java
index cff5126..f5ecda3 100644
--- a/src/com/android/launcher3/qsb/QsbWidgetHostView.java
+++ b/src/com/android/launcher3/qsb/QsbWidgetHostView.java
@@ -16,7 +16,6 @@
 
 package com.android.launcher3.qsb;
 
-import android.appwidget.AppWidgetHostView;
 import android.content.Context;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -26,17 +25,20 @@
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
+import com.android.launcher3.widget.NavigableAppWidgetHostView;
 
 /**
  * Appwidget host view with QSB specific logic.
  */
-public class QsbWidgetHostView extends AppWidgetHostView {
+public class QsbWidgetHostView extends NavigableAppWidgetHostView {
 
     @ViewDebug.ExportedProperty(category = "launcher")
     private int mPreviousOrientation;
 
     public QsbWidgetHostView(Context context) {
         super(context);
+        setFocusable(true);
+        setBackgroundResource(R.drawable.qsb_host_view_focus_bg);
     }
 
     @Override
@@ -89,4 +91,9 @@
                 Launcher.getLauncher(v2.getContext()).startSearch("", false, null, true));
         return v;
     }
+
+    @Override
+    protected boolean shouldAllowDirectClick() {
+        return true;
+    }
 }
diff --git a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
new file mode 100644
index 0000000..a9242f9
--- /dev/null
+++ b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
@@ -0,0 +1,287 @@
+/*
+ * 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.settings;
+
+import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.PLUGIN_CHANGED;
+import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.pluginEnabledKey;
+
+import android.annotation.TargetApi;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+
+import com.android.launcher3.R;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.config.FlagTogglerPrefUi;
+import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
+
+import java.util.List;
+import java.util.Set;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceDataStore;
+import androidx.preference.PreferenceFragment;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.PreferenceViewHolder;
+import androidx.preference.SwitchPreference;
+
+/**
+ * Dev-build only UI allowing developers to toggle flag settings and plugins.
+ * See {@link FeatureFlags}.
+ */
+@TargetApi(Build.VERSION_CODES.O)
+public class DeveloperOptionsFragment extends PreferenceFragment {
+
+    private static final String ACTION_PLUGIN_SETTINGS = "com.android.systemui.action.PLUGIN_SETTINGS";
+    private static final String PLUGIN_PERMISSION = "com.android.systemui.permission.PLUGIN";
+
+    private final BroadcastReceiver mPluginReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            loadPluginPrefs();
+        }
+    };
+
+    private PreferenceScreen mPreferenceScreen;
+
+    private PreferenceCategory mPluginsCategory;
+    private FlagTogglerPrefUi mFlagTogglerPrefUi;
+
+    @Override
+    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+        IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        filter.addDataScheme("package");
+        getContext().registerReceiver(mPluginReceiver, filter);
+        getContext().registerReceiver(mPluginReceiver,
+                new IntentFilter(Intent.ACTION_USER_UNLOCKED));
+
+        mPreferenceScreen = getPreferenceManager().createPreferenceScreen(getContext());
+        setPreferenceScreen(mPreferenceScreen);
+
+        initFlags();
+        loadPluginPrefs();
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        getContext().unregisterReceiver(mPluginReceiver);
+    }
+
+    private PreferenceCategory newCategory(String title) {
+        PreferenceCategory category = new PreferenceCategory(getContext());
+        category.setOrder(Preference.DEFAULT_ORDER);
+        category.setTitle(title);
+        mPreferenceScreen.addPreference(category);
+        return category;
+    }
+
+    private void initFlags() {
+        if (!FeatureFlags.showFlagTogglerUi(getContext())) {
+            return;
+        }
+
+        mFlagTogglerPrefUi = new FlagTogglerPrefUi(this);
+        mFlagTogglerPrefUi.applyTo(newCategory("Feature flags"));
+    }
+
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        if (mFlagTogglerPrefUi != null) {
+            mFlagTogglerPrefUi.onCreateOptionsMenu(menu);
+        }
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (mFlagTogglerPrefUi != null) {
+            mFlagTogglerPrefUi.onOptionsItemSelected(item);
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    @Override
+    public void onStop() {
+        if (mFlagTogglerPrefUi != null) {
+            mFlagTogglerPrefUi.onStop();
+        }
+        super.onStop();
+    }
+
+    private void loadPluginPrefs() {
+        if (mPluginsCategory != null) {
+            mPreferenceScreen.removePreference(mPluginsCategory);
+        }
+        if (!PluginManagerWrapper.hasPlugins(getActivity())) {
+            mPluginsCategory = null;
+            return;
+        }
+        mPluginsCategory = newCategory("Plugins");
+
+        PluginManagerWrapper manager = PluginManagerWrapper.INSTANCE.get(getContext());
+        Context prefContext = getContext();
+        PackageManager pm = getContext().getPackageManager();
+
+        Set<String> pluginActions = manager.getPluginActions();
+        ArrayMap<String, ArraySet<String>> plugins = new ArrayMap<>();
+        for (String action : pluginActions) {
+            String name = toName(action);
+            List<ResolveInfo> result = pm.queryIntentServices(
+                    new Intent(action), PackageManager.MATCH_DISABLED_COMPONENTS);
+            for (ResolveInfo info : result) {
+                String packageName = info.serviceInfo.packageName;
+                if (!plugins.containsKey(packageName)) {
+                    plugins.put(packageName, new ArraySet<>());
+                }
+                plugins.get(packageName).add(name);
+            }
+        }
+
+        List<PackageInfo> apps = pm.getPackagesHoldingPermissions(new String[]{PLUGIN_PERMISSION},
+                PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.GET_SERVICES);
+        PreferenceDataStore enabled = manager.getPluginEnabler();
+        apps.forEach(app -> {
+            if (!plugins.containsKey(app.packageName)) return;
+            SwitchPreference pref = new PluginPreference(prefContext, app, enabled);
+            pref.setSummary("Plugins: " + toString(plugins.get(app.packageName)));
+            mPluginsCategory.addPreference(pref);
+        });
+    }
+
+    private String toString(ArraySet<String> plugins) {
+        StringBuilder b = new StringBuilder();
+        for (String string : plugins) {
+            if (b.length() != 0) {
+                b.append(", ");
+            }
+            b.append(string);
+        }
+        return b.toString();
+    }
+
+    private String toName(String action) {
+        String str = action.replace("com.android.systemui.action.PLUGIN_", "");
+        StringBuilder b = new StringBuilder();
+        for (String s : str.split("_")) {
+            if (b.length() != 0) {
+                b.append(' ');
+            }
+            b.append(s.substring(0, 1));
+            b.append(s.substring(1).toLowerCase());
+        }
+        return b.toString();
+    }
+
+    private static class PluginPreference extends SwitchPreference {
+        private final boolean mHasSettings;
+        private final PackageInfo mInfo;
+        private final PreferenceDataStore mPluginEnabler;
+
+        public PluginPreference(Context prefContext, PackageInfo info,
+                PreferenceDataStore pluginEnabler) {
+            super(prefContext);
+            PackageManager pm = prefContext.getPackageManager();
+            mHasSettings = pm.resolveActivity(new Intent(ACTION_PLUGIN_SETTINGS)
+                    .setPackage(info.packageName), 0) != null;
+            mInfo = info;
+            mPluginEnabler = pluginEnabler;
+            setTitle(info.applicationInfo.loadLabel(pm));
+            setChecked(isPluginEnabled());
+            setWidgetLayoutResource(R.layout.switch_preference_with_settings);
+        }
+
+        private boolean isEnabled(ComponentName cn) {
+            return mPluginEnabler.getBoolean(pluginEnabledKey(cn), true);
+
+        }
+
+        private boolean isPluginEnabled() {
+            for (int i = 0; i < mInfo.services.length; i++) {
+                ComponentName componentName = new ComponentName(mInfo.packageName,
+                        mInfo.services[i].name);
+                if (!isEnabled(componentName)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        @Override
+        protected boolean persistBoolean(boolean isEnabled) {
+            boolean shouldSendBroadcast = false;
+            for (int i = 0; i < mInfo.services.length; i++) {
+                ComponentName componentName = new ComponentName(mInfo.packageName,
+                        mInfo.services[i].name);
+
+                if (isEnabled(componentName) != isEnabled) {
+                    mPluginEnabler.putBoolean(pluginEnabledKey(componentName), isEnabled);
+                    shouldSendBroadcast = true;
+                }
+            }
+            if (shouldSendBroadcast) {
+                final String pkg = mInfo.packageName;
+                final Intent intent = new Intent(PLUGIN_CHANGED,
+                        pkg != null ? Uri.fromParts("package", pkg, null) : null);
+                getContext().sendBroadcast(intent);
+            }
+            setChecked(isEnabled);
+            return true;
+        }
+
+        @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);
+            holder.findViewById(R.id.settings).setOnClickListener(v -> {
+                ResolveInfo result = v.getContext().getPackageManager().resolveActivity(
+                        new Intent(ACTION_PLUGIN_SETTINGS).setPackage(
+                                mInfo.packageName), 0);
+                if (result != null) {
+                    v.getContext().startActivity(new Intent().setComponent(
+                            new ComponentName(result.activityInfo.packageName,
+                                    result.activityInfo.name)));
+                }
+            });
+            holder.itemView.setOnLongClickListener(v -> {
+                Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+                intent.setData(Uri.fromParts("package", mInfo.packageName, null));
+                getContext().startActivity(intent);
+                return true;
+            });
+        }
+    }
+}
diff --git a/src/com/android/launcher3/settings/NotificationDotsPreference.java b/src/com/android/launcher3/settings/NotificationDotsPreference.java
new file mode 100644
index 0000000..f30470a
--- /dev/null
+++ b/src/com/android/launcher3/settings/NotificationDotsPreference.java
@@ -0,0 +1,140 @@
+/*
+ * 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.settings;
+
+import static com.android.launcher3.settings.SettingsActivity.EXTRA_FRAGMENT_ARG_KEY;
+import static com.android.launcher3.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGS;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.android.launcher3.R;
+import com.android.launcher3.notification.NotificationListener;
+import com.android.launcher3.util.SecureSettingsObserver;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+/**
+ * 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 {
+
+    private boolean mWidgetFrameVisible = false;
+
+    /** Hidden field Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */
+    private static final String NOTIFICATION_ENABLED_LISTENERS = "enabled_notification_listeners";
+
+    public NotificationDotsPreference(
+            Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    public NotificationDotsPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public NotificationDotsPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public NotificationDotsPreference(Context context) {
+        super(context);
+    }
+
+    private void setWidgetFrameVisible(boolean isVisible) {
+        if (mWidgetFrameVisible != isVisible) {
+            mWidgetFrameVisible = isVisible;
+            notifyChanged();
+        }
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+
+        View widgetFrame = holder.findViewById(android.R.id.widget_frame);
+        if (widgetFrame != null) {
+            widgetFrame.setVisibility(mWidgetFrameVisible ? View.VISIBLE : View.GONE);
+        }
+    }
+
+    @Override
+    public void onSettingsChanged(boolean enabled) {
+        int summary = enabled
+                ? R.string.notification_dots_desc_on
+                : R.string.notification_dots_desc_off;
+
+        boolean serviceEnabled = true;
+        if (enabled) {
+            // Check if the listener is enabled or not.
+            String enabledListeners = Settings.Secure.getString(
+                    getContext().getContentResolver(), NOTIFICATION_ENABLED_LISTENERS);
+            ComponentName myListener =
+                    new ComponentName(getContext(), NotificationListener.class);
+            serviceEnabled = enabledListeners != null &&
+                    (enabledListeners.contains(myListener.flattenToString()) ||
+                            enabledListeners.contains(myListener.flattenToShortString()));
+            if (!serviceEnabled) {
+                summary = R.string.title_missing_notification_access;
+            }
+        }
+        setWidgetFrameVisible(!serviceEnabled);
+        setFragment(serviceEnabled ? null : NotificationAccessConfirmation.class.getName());
+        setSummary(summary);
+    }
+
+    public static class NotificationAccessConfirmation
+            extends DialogFragment implements DialogInterface.OnClickListener {
+
+        @Override
+        public Dialog onCreateDialog(Bundle savedInstanceState) {
+            final Context context = getActivity();
+            String msg = context.getString(R.string.msg_missing_notification_access,
+                    context.getString(R.string.derived_app_name));
+            return new AlertDialog.Builder(context)
+                    .setTitle(R.string.title_missing_notification_access)
+                    .setMessage(msg)
+                    .setNegativeButton(android.R.string.cancel, null)
+                    .setPositiveButton(R.string.title_change_settings, this)
+                    .create();
+        }
+
+        @Override
+        public void onClick(DialogInterface dialogInterface, int i) {
+            ComponentName cn = new ComponentName(getActivity(), NotificationListener.class);
+            Bundle showFragmentArgs = new Bundle();
+            showFragmentArgs.putString(EXTRA_FRAGMENT_ARG_KEY, cn.flattenToString());
+
+            Intent intent = new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS)
+                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                    .putExtra(EXTRA_FRAGMENT_ARG_KEY, cn.flattenToString())
+                    .putExtra(EXTRA_SHOW_FRAGMENT_ARGS, showFragmentArgs);
+            getActivity().startActivity(intent);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/settings/PreferenceHighlighter.java b/src/com/android/launcher3/settings/PreferenceHighlighter.java
new file mode 100644
index 0000000..8ba8146
--- /dev/null
+++ b/src/com/android/launcher3/settings/PreferenceHighlighter.java
@@ -0,0 +1,127 @@
+/*
+ * 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.settings;
+
+import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.Property;
+import android.view.View;
+
+import com.android.launcher3.util.Themes;
+
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.ItemDecoration;
+import androidx.recyclerview.widget.RecyclerView.State;
+import androidx.recyclerview.widget.RecyclerView.ViewHolder;
+
+/**
+ * Utility class for highlighting a preference
+ */
+public class PreferenceHighlighter extends ItemDecoration implements Runnable {
+
+    private static final Property<PreferenceHighlighter, Integer> HIGHLIGHT_COLOR =
+            new Property<PreferenceHighlighter, Integer>(Integer.TYPE, "highlightColor") {
+
+                @Override
+                public Integer get(PreferenceHighlighter highlighter) {
+                    return highlighter.mHighlightColor;
+                }
+
+                @Override
+                public void set(PreferenceHighlighter highlighter, Integer value) {
+                    highlighter.mHighlightColor = value;
+                    highlighter.mRv.invalidateItemDecorations();
+                }
+            };
+
+    private static final long HIGHLIGHT_DURATION = 15000L;
+    private static final long HIGHLIGHT_FADE_OUT_DURATION = 500L;
+    private static final long HIGHLIGHT_FADE_IN_DURATION = 200L;
+    private static final int END_COLOR = setColorAlphaBound(Color.WHITE, 0);
+
+    private final Paint mPaint = new Paint();
+    private final RecyclerView mRv;
+    private final int mIndex;
+
+    private boolean mHighLightStarted = false;
+    private int mHighlightColor = END_COLOR;
+
+
+    public PreferenceHighlighter(RecyclerView rv, int index) {
+        mRv = rv;
+        mIndex = index;
+    }
+
+    @Override
+    public void run() {
+        mRv.addItemDecoration(this);
+        mRv.smoothScrollToPosition(mIndex);
+    }
+
+    @Override
+    public void onDraw(Canvas c, RecyclerView parent, State state) {
+        ViewHolder holder = parent.findViewHolderForAdapterPosition(mIndex);
+        if (holder == null) {
+            return;
+        }
+        if (!mHighLightStarted && state.getRemainingScrollVertical() != 0) {
+            // Wait until scrolling stopped
+            return;
+        }
+
+        if (!mHighLightStarted) {
+            // Start highlight
+            int colorTo = setColorAlphaBound(Themes.getColorAccent(mRv.getContext()), 66);
+            ObjectAnimator anim = ObjectAnimator.ofArgb(this, HIGHLIGHT_COLOR, END_COLOR, colorTo);
+            anim.setDuration(HIGHLIGHT_FADE_IN_DURATION);
+            anim.setRepeatMode(ValueAnimator.REVERSE);
+            anim.setRepeatCount(4);
+            anim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    removeHighlight();
+                }
+            });
+            anim.start();
+            mHighLightStarted = true;
+        }
+
+        View view = holder.itemView;
+        mPaint.setColor(mHighlightColor);
+        c.drawRect(0, view.getY(), parent.getWidth(), view.getY() + view.getHeight(), mPaint);
+    }
+
+    private void removeHighlight() {
+        ObjectAnimator anim = ObjectAnimator.ofArgb(
+                this, HIGHLIGHT_COLOR, mHighlightColor, END_COLOR);
+        anim.setDuration(HIGHLIGHT_FADE_OUT_DURATION);
+        anim.setStartDelay(HIGHLIGHT_DURATION);
+        anim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mRv.removeItemDecoration(PreferenceHighlighter.this);
+            }
+        });
+        anim.start();
+    }
+}
diff --git a/src/com/android/launcher3/settings/SettingsActivity.java b/src/com/android/launcher3/settings/SettingsActivity.java
new file mode 100644
index 0000000..18b6094
--- /dev/null
+++ b/src/com/android/launcher3/settings/SettingsActivity.java
@@ -0,0 +1,280 @@
+/*
+ * 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.settings;
+
+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.app.Activity;
+import android.app.DialogFragment;
+import android.app.Fragment;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.text.TextUtils;
+
+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.graphics.GridOptionsProvider;
+import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
+import com.android.launcher3.util.SecureSettingsObserver;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceFragment;
+import androidx.preference.PreferenceFragment.OnPreferenceStartFragmentCallback;
+import androidx.preference.PreferenceFragment.OnPreferenceStartScreenCallback;
+import androidx.preference.PreferenceGroup.PreferencePositionCallback;
+import androidx.preference.PreferenceScreen;
+import androidx.recyclerview.widget.RecyclerView;
+
+/**
+ * Settings activity for Launcher. Currently implements the following setting: Allow rotation
+ */
+public class SettingsActivity extends Activity
+        implements OnPreferenceStartFragmentCallback, OnPreferenceStartScreenCallback,
+        SharedPreferences.OnSharedPreferenceChangeListener{
+
+    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";
+
+    public static final String GRID_OPTIONS_PREFERENCE_KEY = "pref_grid_options";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        if (savedInstanceState == null) {
+            Bundle args = new Bundle();
+            String prefKey = getIntent().getStringExtra(EXTRA_FRAGMENT_ARG_KEY);
+            if (!TextUtils.isEmpty(prefKey)) {
+                args.putString(EXTRA_FRAGMENT_ARG_KEY, prefKey);
+            }
+
+            Fragment f = Fragment.instantiate(
+                    this, getString(R.string.settings_fragment_name), args);
+            // Display the fragment as the main content.
+            getFragmentManager().beginTransaction()
+                    .replace(android.R.id.content, f)
+                    .commit();
+        }
+        Utilities.getPrefs(getApplicationContext()).registerOnSharedPreferenceChangeListener(this);
+    }
+    @Override
+    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+        if (GRID_OPTIONS_PREFERENCE_KEY.equals(key)) {
+
+            final ComponentName cn = new ComponentName(getApplicationContext(),
+                    GridOptionsProvider.class);
+            Context c = getApplicationContext();
+            int oldValue = c.getPackageManager().getComponentEnabledSetting(cn);
+            int newValue;
+            if (Utilities.getPrefs(c).getBoolean(GRID_OPTIONS_PREFERENCE_KEY, false)) {
+                newValue = PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+            } else {
+                newValue = PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+            }
+
+            if (oldValue != newValue) {
+                c.getPackageManager().setComponentEnabledSetting(cn, newValue,
+                        PackageManager.DONT_KILL_APP);
+            }
+        }
+    }
+
+    private boolean startFragment(String fragment, Bundle args, String key) {
+        if (Utilities.ATLEAST_P && getFragmentManager().isStateSaved()) {
+            // Sometimes onClick can come after onPause because of being posted on the handler.
+            // Skip starting new fragments in that case.
+            return false;
+        }
+        Fragment f = Fragment.instantiate(this, fragment, args);
+        if (f instanceof DialogFragment) {
+            ((DialogFragment) f).show(getFragmentManager(), key);
+        } else {
+            getFragmentManager()
+                    .beginTransaction()
+                    .replace(android.R.id.content, f)
+                    .addToBackStack(key)
+                    .commit();
+        }
+        return true;
+    }
+
+    @Override
+    public boolean onPreferenceStartFragment(
+            PreferenceFragment preferenceFragment, Preference pref) {
+        return startFragment(pref.getFragment(), pref.getExtras(), pref.getKey());
+    }
+
+    @Override
+    public boolean onPreferenceStartScreen(PreferenceFragment caller, PreferenceScreen pref) {
+        Bundle args = new Bundle();
+        args.putString(PreferenceFragment.ARG_PREFERENCE_ROOT, pref.getKey());
+        return startFragment(getString(R.string.settings_fragment_name), args, pref.getKey());
+    }
+
+    /**
+     * This fragment shows the launcher preferences.
+     */
+    public static class LauncherSettingsFragment extends PreferenceFragment {
+
+        private SecureSettingsObserver mNotificationDotsObserver;
+
+        private String mHighLightKey;
+        private boolean mPreferenceHighlighted = false;
+
+        @Override
+        public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+            final Bundle args = getArguments();
+            mHighLightKey = args == null ? null : args.getString(EXTRA_FRAGMENT_ARG_KEY);
+            if (rootKey == null && !TextUtils.isEmpty(mHighLightKey)) {
+                rootKey = getParentKeyForPref(mHighLightKey);
+            }
+
+            if (savedInstanceState != null) {
+                mPreferenceHighlighted = savedInstanceState.getBoolean(SAVE_HIGHLIGHTED_KEY);
+            }
+
+            getPreferenceManager().setSharedPreferencesName(LauncherFiles.SHARED_PREFERENCES_KEY);
+            setPreferencesFromResource(R.xml.launcher_preferences, rootKey);
+
+            PreferenceScreen screen = getPreferenceScreen();
+            for (int i = screen.getPreferenceCount() - 1; i >= 0; i--) {
+                Preference preference = screen.getPreference(i);
+                if (!initPreference(preference)) {
+                    screen.removePreference(preference);
+                }
+            }
+        }
+
+        @Override
+        public void onSaveInstanceState(Bundle outState) {
+            super.onSaveInstanceState(outState);
+            outState.putBoolean(SAVE_HIGHLIGHTED_KEY, mPreferenceHighlighted);
+        }
+
+        protected String getParentKeyForPref(String key) {
+            return null;
+        }
+
+        /**
+         * Initializes a preference. This is called for every preference. Returning false here
+         * will remove that preference from the list.
+         */
+        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;
+
+                case ALLOW_ROTATION_PREFERENCE_KEY:
+                    if (getResources().getBoolean(R.bool.allow_rotation)) {
+                        // Launcher supports rotation by default. No need to show this setting.
+                        return false;
+                    }
+                    // Initialize the UI once
+                    preference.setDefaultValue(getAllowRotationDefaultValue());
+                    return true;
+
+                case FLAGS_PREFERENCE_KEY:
+                    // Only show flag toggler UI if this build variant implements that.
+                    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());
+                case GRID_OPTIONS_PREFERENCE_KEY:
+                    return Utilities.isDevelopersOptionsEnabled(getContext()) &&
+                            Utilities.IS_DEBUG_DEVICE &&
+                            Utilities.existsStyleWallpapers(getContext());
+            }
+
+            return true;
+        }
+
+        @Override
+        public void onResume() {
+            super.onResume();
+
+            if (isAdded() && !mPreferenceHighlighted) {
+                PreferenceHighlighter highlighter = createHighlighter();
+                if (highlighter != null) {
+                    getView().postDelayed(highlighter, DELAY_HIGHLIGHT_DURATION_MILLIS);
+                    mPreferenceHighlighted = true;
+                }
+            }
+        }
+
+        private PreferenceHighlighter createHighlighter() {
+            if (TextUtils.isEmpty(mHighLightKey)) {
+                return null;
+            }
+
+            PreferenceScreen screen = getPreferenceScreen();
+            if (screen == null) {
+                return null;
+            }
+
+            RecyclerView list = getListView();
+            PreferencePositionCallback callback = (PreferencePositionCallback) list.getAdapter();
+            int position = callback.getPreferenceAdapterPosition(mHighLightKey);
+            return position >= 0 ? new PreferenceHighlighter(list, position) : null;
+        }
+
+        @Override
+        public void onDestroy() {
+            if (mNotificationDotsObserver != null) {
+                mNotificationDotsObserver.unregister();
+                mNotificationDotsObserver = null;
+            }
+            super.onDestroy();
+        }
+    }
+}
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutManager.java b/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
deleted file mode 100644
index 24e2e2f..0000000
--- a/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
+++ /dev/null
@@ -1,253 +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.shortcuts;
-
-import android.annotation.TargetApi;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.LauncherApps;
-import android.content.pm.LauncherApps.ShortcutQuery;
-import android.content.pm.ShortcutInfo;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.util.Log;
-
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.Utilities;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Performs operations related to deep shortcuts, such as querying for them, pinning them, etc.
- */
-public class DeepShortcutManager {
-    private static final String TAG = "DeepShortcutManager";
-
-    private static final int FLAG_GET_ALL = ShortcutQuery.FLAG_MATCH_DYNAMIC
-            | ShortcutQuery.FLAG_MATCH_MANIFEST | ShortcutQuery.FLAG_MATCH_PINNED;
-
-    private static DeepShortcutManager sInstance;
-    private static final Object sInstanceLock = new Object();
-
-    public static DeepShortcutManager getInstance(Context context) {
-        synchronized (sInstanceLock) {
-            if (sInstance == null) {
-                sInstance = new DeepShortcutManager(context.getApplicationContext());
-            }
-            return sInstance;
-        }
-    }
-
-    private final LauncherApps mLauncherApps;
-    private boolean mWasLastCallSuccess;
-
-    private DeepShortcutManager(Context context) {
-        mLauncherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
-    }
-
-    public static boolean supportsShortcuts(ItemInfo info) {
-        boolean isItemPromise = info instanceof com.android.launcher3.ShortcutInfo
-                && ((com.android.launcher3.ShortcutInfo) info).hasPromiseIconUi();
-        return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
-                && !info.isDisabled() && !isItemPromise;
-    }
-
-    public boolean wasLastCallSuccess() {
-        return mWasLastCallSuccess;
-    }
-
-    public void onShortcutsChanged(List<ShortcutInfoCompat> shortcuts) {
-        // mShortcutCache.removeShortcuts(shortcuts);
-    }
-
-    /**
-     * Queries for the shortcuts with the package name and provided ids.
-     *
-     * This method is intended to get the full details for shortcuts when they are added or updated,
-     * because we only get "key" fields in onShortcutsChanged().
-     */
-    public List<ShortcutInfoCompat> queryForFullDetails(String packageName,
-            List<String> shortcutIds, UserHandle user) {
-        return query(FLAG_GET_ALL, packageName, null, shortcutIds, user);
-    }
-
-    /**
-     * Gets all the manifest and dynamic shortcuts associated with the given package and user,
-     * to be displayed in the shortcuts container on long press.
-     */
-    public List<ShortcutInfoCompat> queryForShortcutsContainer(ComponentName activity,
-            List<String> ids, UserHandle user) {
-        return query(ShortcutQuery.FLAG_MATCH_MANIFEST | ShortcutQuery.FLAG_MATCH_DYNAMIC,
-                activity.getPackageName(), activity, ids, user);
-    }
-
-    /**
-     * Removes the given shortcut from the current list of pinned shortcuts.
-     * (Runs on background thread)
-     */
-    @TargetApi(25)
-    public void unpinShortcut(final ShortcutKey key) {
-        if (Utilities.ATLEAST_NOUGAT_MR1) {
-            String packageName = key.componentName.getPackageName();
-            String id = key.getId();
-            UserHandle user = key.user;
-            List<String> pinnedIds = extractIds(queryForPinnedShortcuts(packageName, user));
-            pinnedIds.remove(id);
-            try {
-                mLauncherApps.pinShortcuts(packageName, pinnedIds, user);
-                mWasLastCallSuccess = true;
-            } catch (SecurityException|IllegalStateException e) {
-                Log.w(TAG, "Failed to unpin shortcut", e);
-                mWasLastCallSuccess = false;
-            }
-        }
-    }
-
-    /**
-     * Adds the given shortcut to the current list of pinned shortcuts.
-     * (Runs on background thread)
-     */
-    @TargetApi(25)
-    public void pinShortcut(final ShortcutKey key) {
-        if (Utilities.ATLEAST_NOUGAT_MR1) {
-            String packageName = key.componentName.getPackageName();
-            String id = key.getId();
-            UserHandle user = key.user;
-            List<String> pinnedIds = extractIds(queryForPinnedShortcuts(packageName, user));
-            pinnedIds.add(id);
-            try {
-                mLauncherApps.pinShortcuts(packageName, pinnedIds, user);
-                mWasLastCallSuccess = true;
-            } catch (SecurityException|IllegalStateException e) {
-                Log.w(TAG, "Failed to pin shortcut", e);
-                mWasLastCallSuccess = false;
-            }
-        }
-    }
-
-    @TargetApi(25)
-    public void startShortcut(String packageName, String id, Rect sourceBounds,
-          Bundle startActivityOptions, UserHandle user) {
-        if (Utilities.ATLEAST_NOUGAT_MR1) {
-            try {
-                mLauncherApps.startShortcut(packageName, id, sourceBounds,
-                        startActivityOptions, user);
-                mWasLastCallSuccess = true;
-            } catch (SecurityException|IllegalStateException e) {
-                Log.e(TAG, "Failed to start shortcut", e);
-                mWasLastCallSuccess = false;
-            }
-        }
-    }
-
-    @TargetApi(25)
-    public Drawable getShortcutIconDrawable(ShortcutInfoCompat shortcutInfo, int density) {
-        if (Utilities.ATLEAST_NOUGAT_MR1) {
-            try {
-                Drawable icon = mLauncherApps.getShortcutIconDrawable(
-                        shortcutInfo.getShortcutInfo(), density);
-                mWasLastCallSuccess = true;
-                return icon;
-            } catch (SecurityException|IllegalStateException e) {
-                Log.e(TAG, "Failed to get shortcut icon", e);
-                mWasLastCallSuccess = false;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Returns the id's of pinned shortcuts associated with the given package and user.
-     *
-     * If packageName is null, returns all pinned shortcuts regardless of package.
-     */
-    public List<ShortcutInfoCompat> queryForPinnedShortcuts(String packageName, UserHandle user) {
-        return queryForPinnedShortcuts(packageName, null, user);
-    }
-
-    public List<ShortcutInfoCompat> queryForPinnedShortcuts(String packageName,
-            List<String> shortcutIds, UserHandle user) {
-        return query(ShortcutQuery.FLAG_MATCH_PINNED, packageName, null, shortcutIds, user);
-    }
-
-    public List<ShortcutInfoCompat> queryForAllShortcuts(UserHandle user) {
-        return query(FLAG_GET_ALL, null, null, null, user);
-    }
-
-    private List<String> extractIds(List<ShortcutInfoCompat> shortcuts) {
-        List<String> shortcutIds = new ArrayList<>(shortcuts.size());
-        for (ShortcutInfoCompat shortcut : shortcuts) {
-            shortcutIds.add(shortcut.getId());
-        }
-        return shortcutIds;
-    }
-
-    /**
-     * Query the system server for all the shortcuts matching the given parameters.
-     * If packageName == null, we query for all shortcuts with the passed flags, regardless of app.
-     *
-     * TODO: Use the cache to optimize this so we don't make an RPC every time.
-     */
-    @TargetApi(25)
-    private List<ShortcutInfoCompat> query(int flags, String packageName,
-            ComponentName activity, List<String> shortcutIds, UserHandle user) {
-        if (Utilities.ATLEAST_NOUGAT_MR1) {
-            ShortcutQuery q = new ShortcutQuery();
-            q.setQueryFlags(flags);
-            if (packageName != null) {
-                q.setPackage(packageName);
-                q.setActivity(activity);
-                q.setShortcutIds(shortcutIds);
-            }
-            List<ShortcutInfo> shortcutInfos = null;
-            try {
-                shortcutInfos = mLauncherApps.getShortcuts(q, user);
-                mWasLastCallSuccess = true;
-            } catch (SecurityException|IllegalStateException e) {
-                Log.e(TAG, "Failed to query for shortcuts", e);
-                mWasLastCallSuccess = false;
-            }
-            if (shortcutInfos == null) {
-                return Collections.EMPTY_LIST;
-            }
-            List<ShortcutInfoCompat> shortcutInfoCompats = new ArrayList<>(shortcutInfos.size());
-            for (ShortcutInfo shortcutInfo : shortcutInfos) {
-                shortcutInfoCompats.add(new ShortcutInfoCompat(shortcutInfo));
-            }
-            return shortcutInfoCompats;
-        } else {
-            return Collections.EMPTY_LIST;
-        }
-    }
-
-    @TargetApi(25)
-    public boolean hasHostPermission() {
-        if (Utilities.ATLEAST_NOUGAT_MR1) {
-            try {
-                return mLauncherApps.hasShortcutHostPermission();
-            } catch (SecurityException|IllegalStateException e) {
-                Log.e(TAG, "Failed to make shortcut manager call", e);
-            }
-        }
-        return false;
-    }
-}
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java b/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java
index c809f27..2daa2fe 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java
@@ -18,8 +18,10 @@
 
 import android.content.Context;
 import android.content.res.Resources;
+import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.widget.Toast;
@@ -38,6 +40,10 @@
 
     private Toast mInstructionToast;
 
+    private boolean mShowLoadingState;
+    private Drawable mLoadingStatePlaceholder;
+    private final Rect mLoadingStateBounds = new Rect();
+
     public DeepShortcutTextView(Context context) {
         this(context, null, 0);
     }
@@ -53,6 +59,7 @@
         mDragHandleWidth = resources.getDimensionPixelSize(R.dimen.popup_padding_end)
                 + resources.getDimensionPixelSize(R.dimen.deep_shortcut_drag_handle_size)
                 + resources.getDimensionPixelSize(R.dimen.deep_shortcut_drawable_padding) / 2;
+        showLoadingState(true);
     }
 
     @Override
@@ -63,6 +70,25 @@
         if (!Utilities.isRtl(getResources())) {
             mDragHandleBounds.offset(getMeasuredWidth() - mDragHandleBounds.width(), 0);
         }
+
+        setLoadingBounds();
+    }
+
+    private void setLoadingBounds() {
+        if (mLoadingStatePlaceholder == null) {
+            return;
+        }
+        mLoadingStateBounds.set(
+                0,
+                0,
+                getMeasuredWidth() - mDragHandleWidth - getPaddingStart(),
+                mLoadingStatePlaceholder.getIntrinsicHeight());
+        mLoadingStateBounds.offset(
+                Utilities.isRtl(getResources()) ? mDragHandleWidth : getPaddingStart(),
+                (int) ((getMeasuredHeight() - mLoadingStatePlaceholder.getIntrinsicHeight())
+                        / 2.0f)
+        );
+        mLoadingStatePlaceholder.setBounds(mLoadingStateBounds);
     }
 
     @Override
@@ -71,6 +97,15 @@
     }
 
     @Override
+    public void setText(CharSequence text, BufferType type) {
+        super.setText(text, type);
+
+        if (!TextUtils.isEmpty(text)) {
+            showLoadingState(false);
+        }
+    }
+
+    @Override
     public boolean onTouchEvent(MotionEvent ev) {
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
             // Show toast if user touches the drag handle (long clicks still start the drag).
@@ -88,6 +123,34 @@
         return super.performClick();
     }
 
+    @Override
+    public void onDraw(Canvas canvas) {
+        if (!mShowLoadingState) {
+            super.onDraw(canvas);
+            return;
+        }
+
+        mLoadingStatePlaceholder.draw(canvas);
+    }
+
+    private void showLoadingState(boolean loading) {
+        if (loading == mShowLoadingState) {
+            return;
+        }
+
+        mShowLoadingState = loading;
+
+        if (loading) {
+            mLoadingStatePlaceholder = getContext().getDrawable(
+                    R.drawable.deep_shortcuts_text_placeholder);
+            setLoadingBounds();
+        } else {
+            mLoadingStatePlaceholder = null;
+        }
+
+        invalidate();
+    }
+
     private void showToast() {
         if (mInstructionToast != null) {
             mInstructionToast.cancel();
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutView.java b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
index 9ad266b..9274d44 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutView.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
@@ -17,8 +17,8 @@
 package com.android.launcher3.shortcuts;
 
 import android.content.Context;
+import android.content.pm.ShortcutInfo;
 import android.graphics.Point;
-import android.graphics.Rect;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.View;
@@ -27,10 +27,9 @@
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.popup.PopupContainerWithArrow;
-import com.android.launcher3.touch.ItemClickHandler;
 
 /**
  * A {@link android.widget.FrameLayout} that contains a {@link DeepShortcutView}.
@@ -40,14 +39,12 @@
 
     private static final Point sTempPoint = new Point();
 
-    private final Rect mPillRect;
-
     private BubbleTextView mBubbleText;
     private View mIconView;
     private View mDivider;
 
-    private ShortcutInfo mInfo;
-    private ShortcutInfoCompat mDetail;
+    private WorkspaceItemInfo mInfo;
+    private ShortcutInfo mDetail;
 
     public DeepShortcutView(Context context) {
         this(context, null, 0);
@@ -59,8 +56,6 @@
 
     public DeepShortcutView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
-
-        mPillRect = new Rect();
     }
 
     @Override
@@ -98,18 +93,12 @@
         return sTempPoint;
     }
 
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        mPillRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
-    }
-
     /** package private **/
-    public void applyShortcutInfo(ShortcutInfo info, ShortcutInfoCompat detail,
+    public void applyShortcutInfo(WorkspaceItemInfo info, ShortcutInfo detail,
             PopupContainerWithArrow container) {
         mInfo = info;
         mDetail = detail;
-        mBubbleText.applyFromShortcutInfo(info);
+        mBubbleText.applyFromWorkspaceItem(info);
         mIconView.setBackground(mBubbleText.getIcon());
 
         // Use the long label as long as it exists and fits.
@@ -121,7 +110,7 @@
         mBubbleText.setText(usingLongLabel ? longLabel : mDetail.getShortLabel());
 
         // TODO: Add the click handler to this view directly and not the child view.
-        mBubbleText.setOnClickListener(ItemClickHandler.INSTANCE);
+        mBubbleText.setOnClickListener(container.getItemClickListener());
         mBubbleText.setOnLongClickListener(container);
         mBubbleText.setOnTouchListener(container);
     }
@@ -129,16 +118,20 @@
     /**
      * Returns the shortcut info that is suitable to be added on the homescreen
      */
-    public ShortcutInfo getFinalInfo() {
-        final ShortcutInfo badged = new ShortcutInfo(mInfo);
+    public WorkspaceItemInfo getFinalInfo() {
+        final WorkspaceItemInfo badged = new WorkspaceItemInfo(mInfo);
         // Queue an update task on the worker thread. This ensures that the badged
         // shortcut eventually gets its icon updated.
         Launcher.getLauncher(getContext()).getModel()
-                .updateAndBindShortcutInfo(badged, mDetail);
+                .updateAndBindWorkspaceItem(badged, mDetail);
         return badged;
     }
 
     public View getIconView() {
         return mIconView;
     }
+
+    public ShortcutInfo getDetail() {
+        return mDetail;
+    }
 }
diff --git a/src/com/android/launcher3/shortcuts/ShortcutCache.java b/src/com/android/launcher3/shortcuts/ShortcutCache.java
deleted file mode 100644
index 5742d1d..0000000
--- a/src/com/android/launcher3/shortcuts/ShortcutCache.java
+++ /dev/null
@@ -1,73 +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.shortcuts;
-
-import android.annotation.TargetApi;
-import android.os.Build;
-import android.os.UserHandle;
-import android.util.ArrayMap;
-import android.util.LruCache;
-import java.util.List;
-
-/**
- * Loads {@link ShortcutInfoCompat}s on demand (e.g. when launcher
- * loads for pinned shortcuts and on long-press for dynamic shortcuts), and caches them
- * for handful of apps in an LruCache while launcher lives.
- */
-@TargetApi(Build.VERSION_CODES.N)
-public class ShortcutCache {
-    private static final int CACHE_SIZE = 30; // Max number shortcuts we cache.
-
-    private final LruCache<ShortcutKey, ShortcutInfoCompat> mCachedShortcuts;
-    // We always keep pinned shortcuts in the cache.
-    private final ArrayMap<ShortcutKey, ShortcutInfoCompat> mPinnedShortcuts;
-
-    public ShortcutCache() {
-        mCachedShortcuts = new LruCache<>(CACHE_SIZE);
-        mPinnedShortcuts = new ArrayMap<>();
-    }
-
-    /**
-     * Removes shortcuts from the cache when shortcuts change for a given package.
-     *
-     * Returns a map of ids to their evicted shortcuts.
-     *
-     * @see android.content.pm.LauncherApps.Callback#onShortcutsChanged(String, List, UserHandle).
-     */
-    public void removeShortcuts(List<ShortcutInfoCompat> shortcuts) {
-        for (ShortcutInfoCompat shortcut : shortcuts) {
-            ShortcutKey key = ShortcutKey.fromInfo(shortcut);
-            mCachedShortcuts.remove(key);
-            mPinnedShortcuts.remove(key);
-        }
-    }
-
-    public ShortcutInfoCompat get(ShortcutKey key) {
-        if (mPinnedShortcuts.containsKey(key)) {
-            return mPinnedShortcuts.get(key);
-        }
-        return mCachedShortcuts.get(key);
-    }
-
-    public void put(ShortcutKey key, ShortcutInfoCompat shortcut) {
-        if (shortcut.isPinned()) {
-            mPinnedShortcuts.put(key, shortcut);
-        } else {
-            mCachedShortcuts.put(key, shortcut);
-        }
-    }
-}
diff --git a/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java b/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java
deleted file mode 100644
index 325777d..0000000
--- a/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java
+++ /dev/null
@@ -1,128 +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.shortcuts;
-
-import android.annotation.TargetApi;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ShortcutInfo;
-import android.os.Build;
-import android.os.UserHandle;
-
-import com.android.launcher3.R;
-
-/**
- * Wrapper class for {@link android.content.pm.ShortcutInfo}, representing deep shortcuts into apps.
- *
- * Not to be confused with {@link com.android.launcher3.ShortcutInfo}.
- */
-@TargetApi(Build.VERSION_CODES.N)
-public class ShortcutInfoCompat {
-    private static final String INTENT_CATEGORY = "com.android.launcher3.DEEP_SHORTCUT";
-    private static final String EXTRA_BADGEPKG = "badge_package";
-    public static final String EXTRA_SHORTCUT_ID = "shortcut_id";
-    private ShortcutInfo mShortcutInfo;
-
-    public ShortcutInfoCompat(ShortcutInfo shortcutInfo) {
-        mShortcutInfo = shortcutInfo;
-    }
-
-    @TargetApi(Build.VERSION_CODES.N)
-    public Intent makeIntent() {
-        return new Intent(Intent.ACTION_MAIN)
-                .addCategory(INTENT_CATEGORY)
-                .setComponent(getActivity())
-                .setPackage(getPackage())
-                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
-                .putExtra(EXTRA_SHORTCUT_ID, getId());
-    }
-
-    public ShortcutInfo getShortcutInfo() {
-        return mShortcutInfo;
-    }
-
-    public String getPackage() {
-        return mShortcutInfo.getPackage();
-    }
-
-    public String getBadgePackage(Context context) {
-        String whitelistedPkg = context.getString(R.string.shortcutinfocompat_badgepkg_whitelist);
-        if (whitelistedPkg.equals(getPackage())
-                && mShortcutInfo.getExtras().containsKey(EXTRA_BADGEPKG)) {
-            return mShortcutInfo.getExtras().getString(EXTRA_BADGEPKG);
-        }
-        return getPackage();
-    }
-
-    public String getId() {
-        return mShortcutInfo.getId();
-    }
-
-    public CharSequence getShortLabel() {
-        return mShortcutInfo.getShortLabel();
-    }
-
-    public CharSequence getLongLabel() {
-        return mShortcutInfo.getLongLabel();
-    }
-
-    public long getLastChangedTimestamp() {
-        return mShortcutInfo.getLastChangedTimestamp();
-    }
-
-    public ComponentName getActivity() {
-        return mShortcutInfo.getActivity();
-    }
-
-    public UserHandle getUserHandle() {
-        return mShortcutInfo.getUserHandle();
-    }
-
-    public boolean hasKeyFieldsOnly() {
-        return mShortcutInfo.hasKeyFieldsOnly();
-    }
-
-    public boolean isPinned() {
-        return mShortcutInfo.isPinned();
-    }
-
-    public boolean isDeclaredInManifest() {
-        return mShortcutInfo.isDeclaredInManifest();
-    }
-
-    public boolean isEnabled() {
-        return mShortcutInfo.isEnabled();
-    }
-
-    public boolean isDynamic() {
-        return mShortcutInfo.isDynamic();
-    }
-
-    public int getRank() {
-        return mShortcutInfo.getRank();
-    }
-
-    public CharSequence getDisabledMessage() {
-        return mShortcutInfo.getDisabledMessage();
-    }
-
-    @Override
-    public String toString() {
-        return mShortcutInfo.toString();
-    }
-}
diff --git a/src/com/android/launcher3/shortcuts/ShortcutKey.java b/src/com/android/launcher3/shortcuts/ShortcutKey.java
index cbef85a..70665ca 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutKey.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutKey.java
@@ -2,6 +2,7 @@
 
 import android.content.ComponentName;
 import android.content.Intent;
+import android.content.pm.ShortcutInfo;
 import android.os.UserHandle;
 
 import com.android.launcher3.ItemInfo;
@@ -12,6 +13,9 @@
  */
 public class ShortcutKey extends ComponentKey {
 
+    public static final String EXTRA_SHORTCUT_ID = "shortcut_id";
+    private static final String INTENT_CATEGORY = "com.android.launcher3.DEEP_SHORTCUT";
+
     public ShortcutKey(String packageName, UserHandle user, String id) {
         // Use the id as the class name.
         super(new ComponentName(packageName, id), user);
@@ -25,18 +29,26 @@
         return componentName.getClassName();
     }
 
-    public static ShortcutKey fromInfo(ShortcutInfoCompat shortcutInfo) {
+    public static ShortcutKey fromInfo(ShortcutInfo shortcutInfo) {
         return new ShortcutKey(shortcutInfo.getPackage(), shortcutInfo.getUserHandle(),
                 shortcutInfo.getId());
     }
 
     public static ShortcutKey fromIntent(Intent intent, UserHandle user) {
-        String shortcutId = intent.getStringExtra(
-                ShortcutInfoCompat.EXTRA_SHORTCUT_ID);
+        String shortcutId = intent.getStringExtra(EXTRA_SHORTCUT_ID);
         return new ShortcutKey(intent.getPackage(), user, shortcutId);
     }
 
     public static ShortcutKey fromItemInfo(ItemInfo info) {
         return fromIntent(info.getIntent(), info.user);
     }
+
+    public static Intent makeIntent(ShortcutInfo si) {
+        return new Intent(Intent.ACTION_MAIN)
+                .addCategory(INTENT_CATEGORY)
+                .setComponent(si.getActivity())
+                .setPackage(si.getPackage())
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
+                .putExtra(EXTRA_SHORTCUT_ID, si.getId());
+    }
 }
diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java
index e866445..abf90e2 100644
--- a/src/com/android/launcher3/states/RotationHelper.java
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -20,15 +20,17 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
 
-import static com.android.launcher3.Utilities.ATLEAST_NOUGAT;
-
-import android.app.Activity;
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
 import android.content.res.Resources;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
 
+import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.util.UiThreadHelper;
 
 /**
  * Utility class to manage launcher rotation
@@ -38,25 +40,22 @@
     public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation";
 
     public static boolean getAllowRotationDefaultValue() {
-        if (ATLEAST_NOUGAT) {
-            // If the device was scaled, used 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;
-        }
-        return false;
+        // If the device was scaled, used 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 Launcher mLauncher;
     private final SharedPreferences mPrefs;
 
-    private final boolean mIgnoreAutoRotateSettings;
+    private boolean mIgnoreAutoRotateSettings;
     private boolean mAutoRotateEnabled;
 
     /**
@@ -64,23 +63,28 @@
      */
     private int mStateHandlerRequest = REQUEST_NONE;
     /**
+     * Rotation request made by an app transition
+     */
+    private int mCurrentTransitionRequest = REQUEST_NONE;
+    /**
      * Rotation request made by a Launcher State
      */
     private int mCurrentStateRequest = REQUEST_NONE;
 
     // This is used to defer setting rotation flags until the activity is being created
     private boolean mInitialized;
-    public boolean mDestroyed;
+    private boolean mDestroyed;
+    private boolean mRotationHasDifferentUI;
 
     private int mLastActivityFlags = -1;
 
-    public RotationHelper(Activity activity) {
-        mActivity = activity;
+    public RotationHelper(Launcher launcher) {
+        mLauncher = launcher;
 
         // On large devices we do not handle auto-rotate differently.
-        mIgnoreAutoRotateSettings = mActivity.getResources().getBoolean(R.bool.allow_rotation);
+        mIgnoreAutoRotateSettings = mLauncher.getResources().getBoolean(R.bool.allow_rotation);
         if (!mIgnoreAutoRotateSettings) {
-            mPrefs = Utilities.getPrefs(mActivity);
+            mPrefs = Utilities.getPrefs(mLauncher);
             mPrefs.registerOnSharedPreferenceChangeListener(this);
             mAutoRotateEnabled = mPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
                     getAllowRotationDefaultValue());
@@ -89,16 +93,53 @@
         }
     }
 
+    public void setRotationHadDifferentUI(boolean rotationHasDifferentUI) {
+        mRotationHasDifferentUI = rotationHasDifferentUI;
+    }
+
+    public boolean homeScreenCanRotate() {
+        return mRotationHasDifferentUI || mIgnoreAutoRotateSettings || mAutoRotateEnabled
+                || mStateHandlerRequest != REQUEST_NONE
+                || mLauncher.getDeviceProfile().isMultiWindowMode;
+    }
+
+    public void updateRotationAnimation() {
+        if (FeatureFlags.FAKE_LANDSCAPE_UI.get()) {
+            WindowManager.LayoutParams lp = mLauncher.getWindow().getAttributes();
+            int oldAnim = lp.rotationAnimation;
+            lp.rotationAnimation = homeScreenCanRotate()
+                    ? WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE
+                    : WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
+            if (oldAnim != lp.rotationAnimation) {
+                mLauncher.getWindow().setAttributes(lp);
+            }
+        }
+    }
+
     @Override
     public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
+        boolean wasRotationEnabled = mAutoRotateEnabled;
         mAutoRotateEnabled = mPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
                 getAllowRotationDefaultValue());
-        notifyChange();
+        if (mAutoRotateEnabled != wasRotationEnabled) {
+
+            notifyChange();
+            updateRotationAnimation();
+            mLauncher.reapplyUi();
+        }
     }
 
     public void setStateHandlerRequest(int request) {
         if (mStateHandlerRequest != request) {
             mStateHandlerRequest = request;
+            updateRotationAnimation();
+            notifyChange();
+        }
+    }
+
+    public void setCurrentTransitionRequest(int request) {
+        if (mCurrentTransitionRequest != request) {
+            mCurrentTransitionRequest = request;
             notifyChange();
         }
     }
@@ -110,10 +151,18 @@
         }
     }
 
+    // Used by tests only.
+    public void forceAllowRotationForTesting(boolean allowRotation) {
+        mIgnoreAutoRotateSettings =
+                allowRotation || mLauncher.getResources().getBoolean(R.bool.allow_rotation);
+        notifyChange();
+    }
+
     public void initialize() {
         if (!mInitialized) {
             mInitialized = true;
             notifyChange();
+            updateRotationAnimation();
         }
     }
 
@@ -135,6 +184,9 @@
         if (mStateHandlerRequest != REQUEST_NONE) {
             activityFlags = mStateHandlerRequest == REQUEST_LOCK ?
                     SCREEN_ORIENTATION_LOCKED : SCREEN_ORIENTATION_UNSPECIFIED;
+        } else if (mCurrentTransitionRequest != REQUEST_NONE) {
+            activityFlags = mCurrentTransitionRequest == REQUEST_LOCK ?
+                    SCREEN_ORIENTATION_LOCKED : SCREEN_ORIENTATION_UNSPECIFIED;
         } else if (mCurrentStateRequest == REQUEST_LOCK) {
             activityFlags = SCREEN_ORIENTATION_LOCKED;
         } else if (mIgnoreAutoRotateSettings || mCurrentStateRequest == REQUEST_ROTATE
@@ -147,7 +199,7 @@
         }
         if (activityFlags != mLastActivityFlags) {
             mLastActivityFlags = activityFlags;
-            mActivity.setRequestedOrientation(activityFlags);
+            UiThreadHelper.setOrientationAsync(mLauncher, activityFlags);
         }
     }
 
diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java
index 35f7f88..be3e6c9 100644
--- a/src/com/android/launcher3/states/SpringLoadedState.java
+++ b/src/com/android/launcher3/states/SpringLoadedState.java
@@ -41,7 +41,7 @@
     }
 
     @Override
-    public float[] getWorkspaceScaleAndTranslation(Launcher launcher) {
+    public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
         DeviceProfile grid = launcher.getDeviceProfile();
         Workspace ws = launcher.getWorkspace();
         if (ws.getChildCount() == 0) {
@@ -50,7 +50,7 @@
 
         if (grid.isVerticalBarLayout()) {
             float scale = grid.workspaceSpringLoadShrinkFactor;
-            return new float[] {scale, 0, 0};
+            return new ScaleAndTranslation(scale, 0, 0);
         }
 
         float scale = grid.workspaceSpringLoadShrinkFactor;
@@ -69,7 +69,12 @@
         float myCenter = ws.getTop() + halfHeight;
         float cellTopFromCenter = halfHeight - ws.getChildAt(0).getTop();
         float actualCellTop = myCenter - cellTopFromCenter * scale;
-        return new float[] { scale, 0, (desiredCellTop - actualCellTop) / scale};
+        return new ScaleAndTranslation(scale, 0, (desiredCellTop - actualCellTop) / scale);
+    }
+
+    @Override
+    public ScaleAndTranslation getHotseatScaleAndTranslation(Launcher launcher) {
+        return new ScaleAndTranslation(1, 0, 0);
     }
 
     @Override
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
new file mode 100644
index 0000000..d2e1961
--- /dev/null
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -0,0 +1,113 @@
+/*
+ * 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.testing;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.R;
+import com.android.launcher3.allapps.AllAppsStore;
+import com.android.launcher3.util.ResourceBasedOverride;
+
+import java.util.concurrent.ExecutionException;
+
+public class TestInformationHandler implements ResourceBasedOverride {
+
+    public static TestInformationHandler newInstance(Context context) {
+        return Overrides.getObject(TestInformationHandler.class,
+                context, R.string.test_information_handler_class);
+    }
+
+    protected Context mContext;
+    protected DeviceProfile mDeviceProfile;
+    protected LauncherAppState mLauncherAppState;
+    protected Launcher mLauncher;
+
+    public void init(Context context) {
+        mContext = context;
+        mDeviceProfile = InvariantDeviceProfile.INSTANCE.
+                get(context).getDeviceProfile(context);
+        mLauncherAppState = LauncherAppState.getInstanceNoCreate();
+        mLauncher = mLauncherAppState != null ?
+                (Launcher) mLauncherAppState.getModel().getCallback() : null;
+    }
+
+    public Bundle call(String method) {
+        final Bundle response = new Bundle();
+        switch (method) {
+            case TestProtocol.REQUEST_ALL_APPS_TO_OVERVIEW_SWIPE_HEIGHT: {
+                if (mLauncher == null) return null;
+
+                final float progress = LauncherState.OVERVIEW.getVerticalProgress(mLauncher)
+                        - LauncherState.ALL_APPS.getVerticalProgress(mLauncher);
+                final float distance = mLauncher.getAllAppsController().getShiftRange() * progress;
+                response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) distance);
+                break;
+            }
+
+            case TestProtocol.REQUEST_HOME_TO_ALL_APPS_SWIPE_HEIGHT: {
+                if (mLauncher == null) return null;
+
+                final float progress = LauncherState.NORMAL.getVerticalProgress(mLauncher)
+                        - LauncherState.ALL_APPS.getVerticalProgress(mLauncher);
+                final float distance = mLauncher.getAllAppsController().getShiftRange() * progress;
+                response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) distance);
+                break;
+            }
+
+            case TestProtocol.REQUEST_ENABLE_DEBUG_TRACING:
+                TestProtocol.sDebugTracing = true;
+                break;
+
+            case TestProtocol.REQUEST_DISABLE_DEBUG_TRACING:
+                TestProtocol.sDebugTracing = false;
+                break;
+
+            case TestProtocol.REQUEST_FREEZE_APP_LIST:
+                new MainThreadExecutor().execute(() ->
+                        mLauncher.getAppsView().getAppsStore().enableDeferUpdates(
+                                AllAppsStore.DEFER_UPDATES_TEST));
+                break;
+
+            case TestProtocol.REQUEST_UNFREEZE_APP_LIST:
+                new MainThreadExecutor().execute(() ->
+                        mLauncher.getAppsView().getAppsStore().disableDeferUpdates(
+                                AllAppsStore.DEFER_UPDATES_TEST));
+                break;
+
+            case TestProtocol.REQUEST_APP_LIST_FREEZE_FLAGS: {
+                try {
+                    final int deferUpdatesFlags = new MainThreadExecutor().submit(() ->
+                            mLauncher.getAppsView().getAppsStore().getDeferUpdatesFlags()).get();
+                    response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD,
+                            deferUpdatesFlags);
+                } catch (ExecutionException e) {
+                    throw new RuntimeException(e);
+                } catch (InterruptedException e) {
+                    throw new RuntimeException(e);
+                }
+                break;
+            }
+        }
+        return response;
+    }
+}
diff --git a/src/com/android/launcher3/testing/TestInformationProvider.java b/src/com/android/launcher3/testing/TestInformationProvider.java
new file mode 100644
index 0000000..bd177c0
--- /dev/null
+++ b/src/com/android/launcher3/testing/TestInformationProvider.java
@@ -0,0 +1,67 @@
+/*
+ * 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.testing;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+
+import com.android.launcher3.Utilities;
+
+public class TestInformationProvider extends ContentProvider {
+    @Override
+    public boolean onCreate() {
+        return true;
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues contentValues, String s, String[] strings) {
+        return 0;
+    }
+
+    @Override
+    public int delete(Uri uri, String s, String[] strings) {
+        return 0;
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues contentValues) {
+        return null;
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        return null;
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] strings, String s, String[] strings1, String s1) {
+        return null;
+    }
+
+    @Override
+    public Bundle call(String method, String arg, Bundle extras) {
+        if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+            TestInformationHandler handler = TestInformationHandler.newInstance(getContext());
+            handler.init(getContext());
+            return handler.call(method);
+        }
+        return null;
+    }
+}
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
new file mode 100644
index 0000000..6ffc2d9
--- /dev/null
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -0,0 +1,81 @@
+/*
+ * 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.testing;
+
+/**
+ * Protocol for custom accessibility events for communication with UI Automation tests.
+ */
+public final class TestProtocol {
+    public static final String GET_SCROLL_MESSAGE = "TAPL_GET_SCROLL";
+    public static final String SCROLL_Y_FIELD = "scrollY";
+    public static final String STATE_FIELD = "state";
+    public static final String SWITCHED_TO_STATE_MESSAGE = "TAPL_SWITCHED_TO_STATE";
+    public static final String SCROLL_FINISHED_MESSAGE = "TAPL_SCROLL_FINISHED";
+    public static final String PAUSE_DETECTED_MESSAGE = "TAPL_PAUSE_DETECTED";
+    public static final String RESPONSE_MESSAGE_POSTFIX = "_RESPONSE";
+    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 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 String stateOrdinalToString(int ordinal) {
+        switch (ordinal) {
+            case NORMAL_STATE_ORDINAL:
+                return "Normal";
+            case SPRING_LOADED_STATE_ORDINAL:
+                return "SpringLoaded";
+            case OVERVIEW_STATE_ORDINAL:
+                return "Overview";
+            case OVERVIEW_PEEK_STATE_ORDINAL:
+                return "OverviewPeek";
+            case QUICK_SWITCH_STATE_ORDINAL:
+                return "QuickSwitch";
+            case ALL_APPS_STATE_ORDINAL:
+                return "AllApps";
+            case BACKGROUND_APP_STATE_ORDINAL:
+                return "Background";
+            default:
+                return null;
+        }
+    }
+
+    public static final String TEST_INFO_RESPONSE_FIELD = "response";
+
+    public static final String REQUEST_HOME_TO_OVERVIEW_SWIPE_HEIGHT =
+            "home-to-overview-swipe-height";
+    public static final String REQUEST_BACKGROUND_TO_OVERVIEW_SWIPE_HEIGHT =
+            "background-to-overview-swipe-height";
+    public static final String REQUEST_ALL_APPS_TO_OVERVIEW_SWIPE_HEIGHT =
+            "all-apps-to-overview-swipe-height";
+    public static final String REQUEST_HOME_TO_ALL_APPS_SWIPE_HEIGHT =
+            "home-to-all-apps-swipe-height";
+    public static final String REQUEST_FREEZE_APP_LIST = "freeze-app-list";
+    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 boolean sDebugTracing = false;
+    public static final String REQUEST_ENABLE_DEBUG_TRACING = "enable-debug-tracing";
+    public static final String REQUEST_DISABLE_DEBUG_TRACING = "disable-debug-tracing";
+    public static final String NO_ALLAPPS_EVENT_TAG = "b/133867119";
+    public static final String NO_DRAG_TAG = "b/133009122";
+    public static final String NO_START_TAG = "b/132900132";
+    public static final String NO_START_TASK_TAG = "b/133765434";
+    public static final String NO_OVERVIEW_EVENT_TAG = "b/134532571";
+}
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 55f850c..6f53140 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -20,16 +20,18 @@
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.LauncherStateManager.ANIM_ALL;
-import static com.android.launcher3.LauncherStateManager.ATOMIC_COMPONENT;
+import static com.android.launcher3.LauncherStateManager.ATOMIC_OVERVIEW_SCALE_COMPONENT;
 import static com.android.launcher3.LauncherStateManager.NON_ATOMIC_COMPONENT;
 import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
 import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
+import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
 import android.os.SystemClock;
+import android.util.Log;
 import android.view.HapticFeedbackConstants;
 import android.view.MotionEvent;
 
@@ -37,12 +39,11 @@
 import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.LauncherStateManager.AnimationComponents;
-import com.android.launcher3.LauncherStateManager.AnimationConfig;
-import com.android.launcher3.LauncherStateManager.StateHandler;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.AnimatorSetBuilder;
+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;
@@ -65,10 +66,11 @@
      * 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 static final long ATOMIC_DURATION = 200;
+    protected final long ATOMIC_DURATION = getAtomicDuration();
 
     protected final Launcher mLauncher;
     protected final SwipeDetector mDetector;
+    protected final SwipeDetector.Direction mSwipeDirection;
 
     private boolean mNoIntercept;
     protected int mStartContainerType;
@@ -105,12 +107,20 @@
     public AbstractStateChangeTouchController(Launcher l, SwipeDetector.Direction dir) {
         mLauncher = l;
         mDetector = new SwipeDetector(l, this, dir);
+        mSwipeDirection = dir;
+    }
+
+    protected long getAtomicDuration() {
+        return 200;
     }
 
     protected abstract boolean canInterceptTouch(MotionEvent ev);
 
     @Override
     public final boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "onControllerInterceptTouchEvent 1 " + ev);
+        }
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
             mNoIntercept = !canInterceptTouch(ev);
             if (mNoIntercept) {
@@ -140,6 +150,9 @@
             return false;
         }
 
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "onControllerInterceptTouchEvent 2 ");
+        }
         onControllerTouchEvent(ev);
         return mDetector.isDraggingOrSettling();
     }
@@ -211,14 +224,15 @@
         }
 
         if (mAtomicComponentsController != null) {
-            animComponents &= ~ATOMIC_COMPONENT;
+            animComponents &= ~ATOMIC_OVERVIEW_SCALE_COMPONENT;
         }
         mProgressMultiplier = initCurrentAnimation(animComponents);
         mCurrentAnimation.dispatchOnStart();
         return true;
     }
 
-    private boolean goingBetweenNormalAndOverview(LauncherState fromState, LauncherState toState) {
+    protected boolean goingBetweenNormalAndOverview(LauncherState fromState,
+            LauncherState toState) {
         return (fromState == NORMAL || fromState == OVERVIEW)
                 && (toState == NORMAL || toState == OVERVIEW)
                 && mPendingAnimation == null;
@@ -226,15 +240,21 @@
 
     @Override
     public void onDragStart(boolean start) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "onDragStart 1 " + start);
+        }
         mStartState = mLauncher.getStateManager().getState();
         if (mStartState == ALL_APPS) {
             mStartContainerType = LauncherLogProto.ContainerType.ALLAPPS;
         } else if (mStartState == NORMAL) {
             mStartContainerType = getLogContainerTypeForNormalState();
-        } else if (mStartState   == OVERVIEW){
+        } else if (mStartState == OVERVIEW){
             mStartContainerType = LauncherLogProto.ContainerType.TASKSWITCHER;
         }
         if (mCurrentAnimation == null) {
+            if (TestProtocol.sDebugTracing) {
+                Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "onDragStart 2");
+            }
             mFromState = mStartState;
             mToState = null;
             cancelAnimationControllers();
@@ -254,11 +274,12 @@
     }
 
     @Override
-    public boolean onDrag(float displacement, float velocity) {
+    public boolean onDrag(float displacement) {
         float deltaProgress = mProgressMultiplier * (displacement - mDisplacementShift);
         float progress = deltaProgress + mStartProgress;
         updateProgress(progress);
-        boolean isDragTowardPositive = (displacement - mDisplacementShift) < 0;
+        boolean isDragTowardPositive = mSwipeDirection.isPositive(
+                displacement - mDisplacementShift);
         if (progress <= 0) {
             if (reinitCurrentAnimation(false, isDragTowardPositive)) {
                 mDisplacementShift = displacement;
@@ -344,14 +365,8 @@
     private AnimatorSet createAtomicAnimForState(LauncherState fromState, LauncherState targetState,
             long duration) {
         AnimatorSetBuilder builder = getAnimatorSetBuilderForStates(fromState, targetState);
-        mLauncher.getStateManager().prepareForAtomicAnimation(fromState, targetState, builder);
-        AnimationConfig config = new AnimationConfig();
-        config.animComponents = ATOMIC_COMPONENT;
-        config.duration = duration;
-        for (StateHandler handler : mLauncher.getStateManager().getStateHandlers()) {
-            handler.setStateWithAnimation(targetState, builder, config);
-        }
-        return builder.build();
+        return mLauncher.getStateManager().createAtomicAnimation(fromState, targetState, builder,
+                ATOMIC_OVERVIEW_SCALE_COMPONENT, duration);
     }
 
     protected AnimatorSetBuilder getAnimatorSetBuilderForStates(LauncherState fromState,
@@ -361,6 +376,9 @@
 
     @Override
     public void onDragEnd(float velocity, boolean fling) {
+        if (com.android.launcher3.testing.TestProtocol.sDebugTracing) {
+            android.util.Log.e(TestProtocol.NO_ALLAPPS_EVENT_TAG, "onDragEnd");
+        }
         final int logAction = fling ? Touch.FLING : Touch.SWIPE;
 
         boolean blockedFling = fling && mFlingBlockCheck.isBlocked();
@@ -370,8 +388,7 @@
 
         final LauncherState targetState;
         final float progress = mCurrentAnimation.getProgressFraction();
-        final float interpolatedProgress = mCurrentAnimation.getInterpolator()
-                .getInterpolation(progress);
+        final float interpolatedProgress = mCurrentAnimation.getInterpolatedProgress();
         if (fling) {
             targetState =
                     Float.compare(Math.signum(velocity), Math.signum(mProgressMultiplier)) == 0
@@ -427,8 +444,8 @@
         maybeUpdateAtomicAnim(mFromState, targetState, targetState == mToState ? 1f : 0f);
         updateSwipeCompleteAnimation(anim, Math.max(duration, getRemainingAtomicDuration()),
                 targetState, velocity, fling);
-        mCurrentAnimation.dispatchOnStart();
-        if (fling && targetState == LauncherState.ALL_APPS) {
+        mCurrentAnimation.dispatchOnStartWithVelocity(endProgress, velocity);
+        if (fling && targetState == LauncherState.ALL_APPS && !QUICKSTEP_SPRINGS.get()) {
             mLauncher.getAppsView().addSpringFromFlingUpdateListener(anim, velocity);
         }
         anim.start();
@@ -498,6 +515,9 @@
     }
 
     protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
+        if (com.android.launcher3.testing.TestProtocol.sDebugTracing) {
+            android.util.Log.e(TestProtocol.NO_ALLAPPS_EVENT_TAG, "onSwipeInteractionCompleted 1");
+        }
         if (mAtomicComponentsController != null) {
             mAtomicComponentsController.getAnimationPlayer().end();
             mAtomicComponentsController = null;
@@ -515,13 +535,18 @@
                 logReachedState(logAction, targetState);
             }
             mLauncher.getStateManager().goToState(targetState, false /* animated */);
+
+            if (com.android.launcher3.testing.TestProtocol.sDebugTracing) {
+                android.util.Log.e(
+                        TestProtocol.NO_ALLAPPS_EVENT_TAG, "onSwipeInteractionCompleted 2");
+            }
         }
     }
 
     private void logReachedState(int logAction, LauncherState targetState) {
         // Transition complete. log the action
         mLauncher.getUserEventDispatcher().logStateChangeAction(logAction,
-                getDirectionForLog(),
+                getDirectionForLog(), mDetector.getDownX(), mDetector.getDownY(),
                 mStartContainerType,
                 mStartState.containerType,
                 targetState.containerType,
@@ -538,6 +563,9 @@
     }
 
     private void cancelAnimationControllers() {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "cancelAnimationControllers");
+        }
         mCurrentAnimation = null;
         cancelAtomicComponentsController();
         mDetector.finishedScrolling();
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index f2f5592..85f763d 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -22,6 +22,7 @@
 import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
 import static com.android.launcher3.Launcher.REQUEST_BIND_PENDING_APPWIDGET;
 import static com.android.launcher3.Launcher.REQUEST_RECONFIGURE_APPWIDGET;
+import static com.android.launcher3.model.AppLaunchTracker.CONTAINER_ALL_APPS;
 
 import android.app.AlertDialog;
 import android.content.Intent;
@@ -31,6 +32,8 @@
 import android.view.View.OnClickListener;
 import android.widget.Toast;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.FolderInfo;
@@ -40,11 +43,13 @@
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.PromiseAppInfo;
 import com.android.launcher3.R;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.views.FloatingIconView;
 import com.android.launcher3.widget.PendingAppWidgetHostView;
 import com.android.launcher3.widget.WidgetAddFlowHandler;
 
@@ -56,29 +61,50 @@
     /**
      * Instance used for click handling on items
      */
-    public static final OnClickListener INSTANCE = ItemClickHandler::onClick;
+    public static final OnClickListener INSTANCE = getInstance(null);
 
-    private static void onClick(View v) {
+    public static final OnClickListener getInstance(String sourceContainer) {
+        return v -> onClick(v, sourceContainer);
+    }
+
+    private static void onClick(View v, String sourceContainer) {
+        if (TestProtocol.sDebugTracing) {
+            android.util.Log.d(TestProtocol.NO_START_TAG,
+                    "onClick 1");
+        }
         // 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) {
+            if (TestProtocol.sDebugTracing) {
+                android.util.Log.d(TestProtocol.NO_START_TAG,
+                        "onClick 2");
+            }
             return;
         }
 
         Launcher launcher = Launcher.getLauncher(v.getContext());
         if (!launcher.getWorkspace().isFinishedSwitchingState()) {
+            if (TestProtocol.sDebugTracing) {
+                android.util.Log.d(TestProtocol.NO_START_TAG,
+                        "onClick 3");
+            }
             return;
         }
 
         Object tag = v.getTag();
-        if (tag instanceof ShortcutInfo) {
-            onClickAppShortcut(v, (ShortcutInfo) tag, launcher);
+        if (tag instanceof WorkspaceItemInfo) {
+            onClickAppShortcut(v, (WorkspaceItemInfo) tag, launcher, sourceContainer);
         } else if (tag instanceof FolderInfo) {
             if (v instanceof FolderIcon) {
                 onClickFolderIcon(v);
             }
         } else if (tag instanceof AppInfo) {
-            startAppShortcutOrInfoActivity(v, (AppInfo) tag, launcher);
+            if (TestProtocol.sDebugTracing) {
+                android.util.Log.d(TestProtocol.NO_START_TAG,
+                        "onClick 4");
+            }
+            startAppShortcutOrInfoActivity(v, (AppInfo) tag, launcher,
+                    sourceContainer == null ? CONTAINER_ALL_APPS: sourceContainer);
         } else if (tag instanceof LauncherAppWidgetInfo) {
             if (v instanceof PendingAppWidgetHostView) {
                 onClickPendingWidget((PendingAppWidgetHostView) v, launcher);
@@ -154,17 +180,19 @@
     private static void startMarketIntentForPackage(View v, Launcher launcher, String packageName) {
         ItemInfo item = (ItemInfo) v.getTag();
         Intent intent = new PackageManagerHelper(launcher).getMarketIntent(packageName);
-        launcher.startActivitySafely(v, intent, item);
+        launcher.startActivitySafely(v, intent, item, null);
     }
 
     /**
      * Event handler for an app shortcut click.
      *
-     * @param v The view that was clicked. Must be a tagged with a {@link ShortcutInfo}.
+     * @param v The view that was clicked. Must be a tagged with a {@link WorkspaceItemInfo}.
      */
-    private static void onClickAppShortcut(View v, ShortcutInfo shortcut, Launcher launcher) {
+    public static void onClickAppShortcut(View v, WorkspaceItemInfo shortcut, Launcher launcher,
+            @Nullable String sourceContainer) {
         if (shortcut.isDisabled()) {
-            final int disabledFlags = shortcut.runtimeStatusFlags & ShortcutInfo.FLAG_DISABLED_MASK;
+            final int disabledFlags = shortcut.runtimeStatusFlags
+                    & WorkspaceItemInfo.FLAG_DISABLED_MASK;
             if ((disabledFlags &
                     ~FLAG_DISABLED_SUSPENDED &
                     ~FLAG_DISABLED_QUIET_USER) == 0) {
@@ -195,16 +223,21 @@
                     shortcut.intent.getComponent().getPackageName() : shortcut.intent.getPackage();
             if (!TextUtils.isEmpty(packageName)) {
                 onClickPendingAppItem(v, launcher, packageName,
-                        shortcut.hasStatusFlag(ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE));
+                        shortcut.hasStatusFlag(WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE));
                 return;
             }
         }
 
         // Start activities
-        startAppShortcutOrInfoActivity(v, shortcut, launcher);
+        startAppShortcutOrInfoActivity(v, shortcut, launcher, sourceContainer);
     }
 
-    private static void startAppShortcutOrInfoActivity(View v, ItemInfo item, Launcher launcher) {
+    private static void startAppShortcutOrInfoActivity(View v, ItemInfo item, Launcher launcher,
+            @Nullable String sourceContainer) {
+        if (TestProtocol.sDebugTracing) {
+            android.util.Log.d(TestProtocol.NO_START_TAG,
+                    "startAppShortcutOrInfoActivity");
+        }
         Intent intent;
         if (item instanceof PromiseAppInfo) {
             PromiseAppInfo promiseAppInfo = (PromiseAppInfo) item;
@@ -215,10 +248,10 @@
         if (intent == null) {
             throw new IllegalArgumentException("Input must have a valid intent");
         }
-        if (item instanceof ShortcutInfo) {
-            ShortcutInfo si = (ShortcutInfo) item;
-            if (si.hasStatusFlag(ShortcutInfo.FLAG_SUPPORTS_WEB_UI)
-                    && intent.getAction() == Intent.ACTION_VIEW) {
+        if (item instanceof WorkspaceItemInfo) {
+            WorkspaceItemInfo si = (WorkspaceItemInfo) item;
+            if (si.hasStatusFlag(WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI)
+                    && Intent.ACTION_VIEW.equals(intent.getAction())) {
                 // make a copy of the intent that has the package set to null
                 // we do this because the platform sometimes disables instant
                 // apps temporarily (triggered by the user) and fallbacks to the
@@ -227,6 +260,10 @@
                 intent.setPackage(null);
             }
         }
-        launcher.startActivitySafely(v, intent, item);
+        if (v != null && launcher.getAppTransitionManager().supportsAdaptiveIconAnimation()) {
+            // 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);
     }
 }
diff --git a/src/com/android/launcher3/touch/ItemLongClickListener.java b/src/com/android/launcher3/touch/ItemLongClickListener.java
index 6f012f6..babbcdd 100644
--- a/src/com/android/launcher3/touch/ItemLongClickListener.java
+++ b/src/com/android/launcher3/touch/ItemLongClickListener.java
@@ -17,7 +17,6 @@
 
 import static android.view.View.INVISIBLE;
 import static android.view.View.VISIBLE;
-
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
@@ -30,7 +29,6 @@
 import com.android.launcher3.DropTarget;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.folder.Folder;
diff --git a/src/com/android/launcher3/touch/OverScroll.java b/src/com/android/launcher3/touch/OverScroll.java
index dc801ec..bf895ad 100644
--- a/src/com/android/launcher3/touch/OverScroll.java
+++ b/src/com/android/launcher3/touch/OverScroll.java
@@ -20,7 +20,7 @@
  */
 public class OverScroll {
 
-    private static final float OVERSCROLL_DAMP_FACTOR = 0.07f;
+    public static final float OVERSCROLL_DAMP_FACTOR = 0.07f;
 
     /**
      * This curve determines how the effect of scrolling over the limits of the page diminishes
diff --git a/src/com/android/launcher3/touch/SwipeDetector.java b/src/com/android/launcher3/touch/SwipeDetector.java
index 703e4fd..3d45404 100644
--- a/src/com/android/launcher3/touch/SwipeDetector.java
+++ b/src/com/android/launcher3/touch/SwipeDetector.java
@@ -19,12 +19,17 @@
 
 import android.content.Context;
 import android.graphics.PointF;
-import android.support.annotation.NonNull;
-import android.support.annotation.VisibleForTesting;
 import android.util.Log;
 import android.view.MotionEvent;
+import android.view.VelocityTracker;
 import android.view.ViewConfiguration;
 
+import com.android.launcher3.Utilities;
+import com.android.launcher3.testing.TestProtocol;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+
 /**
  * One dimensional scroll/drag/swipe gesture detector.
  *
@@ -51,12 +56,6 @@
      */
     public static final float RELEASE_VELOCITY_PX_MS = 1.0f;
 
-    /**
-     * The time constant used to calculate dampening in the low-pass filter of scroll velocity.
-     * Cutoff frequency is set at 10 Hz.
-     */
-    public static final float SCROLL_VELOCITY_DAMPENING_RC = 1000f / (2f * (float) Math.PI * 10);
-
     /* Scroll state, this is set to true during dragging and animation. */
     private ScrollState mState = ScrollState.IDLE;
 
@@ -68,18 +67,25 @@
 
     public static abstract class Direction {
 
-        abstract float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint);
+        abstract float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint,
+                boolean isRtl);
 
         /**
          * Distance in pixels a touch can wander before we think the user is scrolling.
          */
         abstract float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos);
+
+        abstract float getVelocity(VelocityTracker tracker, boolean isRtl);
+
+        abstract boolean isPositive(float displacement);
+
+        abstract boolean isNegative(float displacement);
     }
 
     public static final Direction VERTICAL = new Direction() {
 
         @Override
-        float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint) {
+        float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint, boolean isRtl) {
             return ev.getY(pointerIndex) - refPoint.y;
         }
 
@@ -87,19 +93,61 @@
         float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos) {
             return Math.abs(ev.getX(pointerIndex) - downPos.x);
         }
+
+        @Override
+        float getVelocity(VelocityTracker tracker, boolean isRtl) {
+            return tracker.getYVelocity();
+        }
+
+        @Override
+        boolean isPositive(float displacement) {
+            // Up
+            return displacement < 0;
+        }
+
+        @Override
+        boolean isNegative(float displacement) {
+            // Down
+            return displacement > 0;
+        }
     };
 
     public static final Direction HORIZONTAL = new Direction() {
 
         @Override
-        float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint) {
-            return ev.getX(pointerIndex) - refPoint.x;
+        float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint, boolean isRtl) {
+            float displacement = ev.getX(pointerIndex) - refPoint.x;
+            if (isRtl) {
+                displacement = -displacement;
+            }
+            return displacement;
         }
 
         @Override
         float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos) {
             return Math.abs(ev.getY(pointerIndex) - downPos.y);
         }
+
+        @Override
+        float getVelocity(VelocityTracker tracker, boolean isRtl) {
+            float velocity = tracker.getXVelocity();
+            if (isRtl) {
+                velocity = -velocity;
+            }
+            return velocity;
+        }
+
+        @Override
+        boolean isPositive(float displacement) {
+            // Right
+            return displacement > 0;
+        }
+
+        @Override
+        boolean isNegative(float displacement) {
+            // Left
+            return displacement < 0;
+        }
     };
 
     //------------------- ScrollState transition diagram -----------------------------------
@@ -110,6 +158,9 @@
     // SETTLING -> (View settled) -> IDLE
 
     private void setState(ScrollState newState) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "setState -- start: " + newState);
+        }
         if (DBG) {
             Log.d(TAG, "setState:" + mState + "->" + newState);
         }
@@ -117,6 +168,9 @@
         if (newState == ScrollState.DRAGGING) {
             initializeDragging();
             if (mState == ScrollState.IDLE) {
+                if (TestProtocol.sDebugTracing) {
+                    Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "setState -- 1: " + newState);
+                }
                 reportDragStart(false /* recatch */);
             } else if (mState == ScrollState.SETTLING) {
                 reportDragStart(true /* recatch */);
@@ -127,12 +181,24 @@
         }
 
         mState = newState;
+        if (com.android.launcher3.testing.TestProtocol.sDebugTracing) {
+            android.util.Log.e(TestProtocol.NO_ALLAPPS_EVENT_TAG,
+                    "setState: " + newState + " @ " + android.util.Log.getStackTraceString(
+                            new Throwable()));
+        }
     }
 
     public boolean isDraggingOrSettling() {
         return mState == ScrollState.DRAGGING || mState == ScrollState.SETTLING;
     }
 
+    public int getDownX() {
+        return (int) mDownPos.x;
+    }
+
+    public int getDownY() {
+        return (int) mDownPos.y;
+    }
     /**
      * There's no touch and there's no animation.
      */
@@ -150,16 +216,17 @@
 
     private final PointF mDownPos = new PointF();
     private final PointF mLastPos = new PointF();
-    private Direction mDir;
+    private final Direction mDir;
+    private final boolean mIsRtl;
 
     private final float mTouchSlop;
+    private final float mMaxVelocity;
 
     /* Client of this gesture detector can register a callback. */
     private final Listener mListener;
 
-    private long mCurrentMillis;
+    private VelocityTracker mVelocityTracker;
 
-    private float mVelocity;
     private float mLastDisplacement;
     private float mDisplacement;
 
@@ -169,24 +236,27 @@
     public interface Listener {
         void onDragStart(boolean start);
 
-        boolean onDrag(float displacement, float velocity);
+        boolean onDrag(float displacement);
+
+        default boolean onDrag(float displacement, MotionEvent event) {
+            return onDrag(displacement);
+        }
 
         void onDragEnd(float velocity, boolean fling);
     }
 
     public SwipeDetector(@NonNull Context context, @NonNull Listener l, @NonNull Direction dir) {
-        this(ViewConfiguration.get(context).getScaledTouchSlop(), l, dir);
+        this(ViewConfiguration.get(context), l, dir, Utilities.isRtl(context.getResources()));
     }
 
     @VisibleForTesting
-    protected SwipeDetector(float touchSlope, @NonNull Listener l, @NonNull Direction dir) {
-        mTouchSlop = touchSlope;
+    protected SwipeDetector(@NonNull ViewConfiguration config, @NonNull Listener l,
+            @NonNull Direction dir, boolean isRtl) {
         mListener = l;
         mDir = dir;
-    }
-
-    public void updateDirection(Direction dir) {
-        mDir = dir;
+        mIsRtl = isRtl;
+        mTouchSlop = config.getScaledTouchSlop();
+        mMaxVelocity = config.getScaledMaximumFlingVelocity();
     }
 
     public void setDetectableScrollConditions(int scrollDirectionFlags, boolean ignoreSlop) {
@@ -206,22 +276,30 @@
         }
 
         // Check if the client is interested in scroll in current direction.
-        if (((mScrollConditions & DIRECTION_NEGATIVE) > 0 && mDisplacement > 0) ||
-                ((mScrollConditions & DIRECTION_POSITIVE) > 0 && mDisplacement < 0)) {
+        if (((mScrollConditions & DIRECTION_NEGATIVE) > 0 && mDir.isNegative(mDisplacement)) ||
+                ((mScrollConditions & DIRECTION_POSITIVE) > 0 && mDir.isPositive(mDisplacement))) {
             return true;
         }
         return false;
     }
 
     public boolean onTouchEvent(MotionEvent ev) {
-        switch (ev.getActionMasked()) {
+        int actionMasked = ev.getActionMasked();
+        if (actionMasked == MotionEvent.ACTION_DOWN && mVelocityTracker != null) {
+            mVelocityTracker.clear();
+        }
+        if (mVelocityTracker == null) {
+            mVelocityTracker = VelocityTracker.obtain();
+        }
+        mVelocityTracker.addMovement(ev);
+
+        switch (actionMasked) {
             case MotionEvent.ACTION_DOWN:
                 mActivePointerId = ev.getPointerId(0);
                 mDownPos.set(ev.getX(), ev.getY());
                 mLastPos.set(mDownPos);
                 mLastDisplacement = 0;
                 mDisplacement = 0;
-                mVelocity = 0;
 
                 if (mState == ScrollState.SETTLING && mIgnoreSlopWhenSettling) {
                     setState(ScrollState.DRAGGING);
@@ -245,16 +323,20 @@
                 if (pointerIndex == INVALID_POINTER_ID) {
                     break;
                 }
-                mDisplacement = mDir.getDisplacement(ev, pointerIndex, mDownPos);
-                computeVelocity(mDir.getDisplacement(ev, pointerIndex, mLastPos),
-                        ev.getEventTime());
+                mDisplacement = mDir.getDisplacement(ev, pointerIndex, mDownPos, mIsRtl);
+                if (TestProtocol.sDebugTracing) {
+                    Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "onTouchEvent 1");
+                }
 
                 // handle state and listener calls.
                 if (mState != ScrollState.DRAGGING && shouldScrollStart(ev, pointerIndex)) {
+                    if (TestProtocol.sDebugTracing) {
+                        Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "onTouchEvent 2");
+                    }
                     setState(ScrollState.DRAGGING);
                 }
                 if (mState == ScrollState.DRAGGING) {
-                    reportDragging();
+                    reportDragging(ev);
                 }
                 mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
                 break;
@@ -264,6 +346,8 @@
                 if (mState == ScrollState.DRAGGING) {
                     setState(ScrollState.SETTLING);
                 }
+                mVelocityTracker.recycle();
+                mVelocityTracker = null;
                 break;
             default:
                 break;
@@ -301,61 +385,30 @@
      * @see #DIRECTION_BOTH
      */
     public boolean wasInitialTouchPositive() {
-        return mSubtractDisplacement < 0;
+        return mDir.isPositive(mSubtractDisplacement);
     }
 
-    private boolean reportDragging() {
+    private boolean reportDragging(MotionEvent event) {
         if (mDisplacement != mLastDisplacement) {
             if (DBG) {
-                Log.d(TAG, String.format("onDrag disp=%.1f, velocity=%.1f",
-                        mDisplacement, mVelocity));
+                Log.d(TAG, String.format("onDrag disp=%.1f", mDisplacement));
             }
 
             mLastDisplacement = mDisplacement;
-            return mListener.onDrag(mDisplacement - mSubtractDisplacement, mVelocity);
+            return mListener.onDrag(mDisplacement - mSubtractDisplacement, event);
         }
         return true;
     }
 
     private void reportDragEnd() {
+        mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
+        float velocity = mDir.getVelocity(mVelocityTracker, mIsRtl) / 1000;
         if (DBG) {
             Log.d(TAG, String.format("onScrollEnd disp=%.1f, velocity=%.1f",
-                    mDisplacement, mVelocity));
+                    mDisplacement, velocity));
         }
-        mListener.onDragEnd(mVelocity, Math.abs(mVelocity) > RELEASE_VELOCITY_PX_MS);
 
-    }
-
-    /**
-     * Computes the damped velocity.
-     */
-    public float computeVelocity(float delta, long currentMillis) {
-        long previousMillis = mCurrentMillis;
-        mCurrentMillis = currentMillis;
-
-        float deltaTimeMillis = mCurrentMillis - previousMillis;
-        float velocity = (deltaTimeMillis > 0) ? (delta / deltaTimeMillis) : 0;
-        if (Math.abs(mVelocity) < 0.001f) {
-            mVelocity = velocity;
-        } else {
-            float alpha = computeDampeningFactor(deltaTimeMillis);
-            mVelocity = interpolate(mVelocity, velocity, alpha);
-        }
-        return mVelocity;
-    }
-
-    /**
-     * Returns a time-dependent dampening factor using delta time.
-     */
-    private static float computeDampeningFactor(float deltaTime) {
-        return deltaTime / (SCROLL_VELOCITY_DAMPENING_RC + deltaTime);
-    }
-
-    /**
-     * Returns the linear interpolation between two values
-     */
-    public static float interpolate(float from, float to, float alpha) {
-        return (1.0f - alpha) * from + alpha * to;
+        mListener.onDragEnd(velocity, Math.abs(velocity) > RELEASE_VELOCITY_PX_MS);
     }
 
     public static long calculateDuration(float velocity, float progressNeeded) {
diff --git a/src/com/android/launcher3/touch/TouchEventTranslator.java b/src/com/android/launcher3/touch/TouchEventTranslator.java
new file mode 100644
index 0000000..3fcda90
--- /dev/null
+++ b/src/com/android/launcher3/touch/TouchEventTranslator.java
@@ -0,0 +1,283 @@
+/*
+ * 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.touch;
+
+import android.graphics.PointF;
+import android.util.Log;
+import android.util.Pair;
+import android.util.SparseArray;
+import android.view.MotionEvent;
+import android.view.MotionEvent.PointerCoords;
+import android.view.MotionEvent.PointerProperties;
+
+import java.util.function.Consumer;
+
+/**
+ * To minimize the size of the MotionEvent, historic events are not copied and passed via the
+ * listener.
+ */
+public class TouchEventTranslator {
+
+    private static final String TAG = "TouchEventTranslator";
+    private static final boolean DEBUG = false;
+
+    private class DownState {
+        long timeStamp;
+        float downX;
+        float downY;
+        public DownState(long timeStamp, float downX, float downY) {
+            this.timeStamp = timeStamp;
+            this.downX = downX;
+            this.downY = downY;
+        }
+    };
+    private final DownState ZERO = new DownState(0, 0f, 0f);
+
+    private final Consumer<MotionEvent> mListener;
+
+    private final SparseArray<DownState> mDownEvents;
+    private final SparseArray<PointF> mFingers;
+
+    private final SparseArray<Pair<PointerProperties[], PointerCoords[]>> mCache;
+
+    public TouchEventTranslator(Consumer<MotionEvent> listener) {
+        mDownEvents = new SparseArray<>();
+        mFingers = new SparseArray<>();
+        mCache = new SparseArray<>();
+
+        mListener = listener;
+    }
+
+    public void reset() {
+        mDownEvents.clear();
+        mFingers.clear();
+    }
+
+    public float getDownX() {
+        return mDownEvents.get(0).downX;
+    }
+
+    public float getDownY() {
+        return mDownEvents.get(0).downY;
+    }
+
+    public void setDownParameters(int idx, MotionEvent e) {
+        DownState ev = new DownState(e.getEventTime(), e.getX(idx), e.getY(idx));
+        mDownEvents.append(idx, ev);
+    }
+
+    public void dispatchDownEvents(MotionEvent ev) {
+        for(int i = 0; i < ev.getPointerCount() && i < mDownEvents.size(); i++) {
+            int pid = ev.getPointerId(i);
+            put(pid, i, ev.getX(i), 0, mDownEvents.get(i).timeStamp, ev);
+        }
+    }
+
+    public void processMotionEvent(MotionEvent ev) {
+        if (DEBUG) {
+            printSamples(TAG + " processMotionEvent", ev);
+        }
+        int index = ev.getActionIndex();
+        float x = ev.getX(index);
+        float y = ev.getY(index) - mDownEvents.get(index, ZERO).downY;
+        switch (ev.getActionMasked()) {
+            case MotionEvent.ACTION_POINTER_DOWN:
+                int pid = ev.getPointerId(index);
+                if(mFingers.get(pid, null) != null) {
+                    for(int i=0; i < ev.getPointerCount(); i++) {
+                        pid = ev.getPointerId(i);
+                        position(pid, x, y);
+                    }
+                    generateEvent(ev.getAction(), ev);
+                } else {
+                    put(pid, index, x, y, ev);
+                }
+                break;
+            case MotionEvent.ACTION_MOVE:
+                for(int i=0; i < ev.getPointerCount(); i++) {
+                    pid = ev.getPointerId(i);
+                    position(pid, x, y);
+                }
+                generateEvent(ev.getAction(), ev);
+                break;
+            case MotionEvent.ACTION_POINTER_UP:
+            case MotionEvent.ACTION_UP:
+                pid = ev.getPointerId(index);
+                lift(pid, index, x, y, ev);
+                break;
+            case MotionEvent.ACTION_CANCEL:
+                cancel(ev);
+                break;
+            default:
+                Log.v(TAG, "Didn't process ");
+                printSamples(TAG, ev);
+
+        }
+    }
+
+    private TouchEventTranslator put(int id, int index, float x, float y, MotionEvent ev) {
+        return put(id, index, x, y, ev.getEventTime(), ev);
+    }
+
+    private TouchEventTranslator put(int id, int index, float x, float y, long ms, MotionEvent ev) {
+        checkFingerExistence(id, false);
+        boolean isInitialDown = (mFingers.size() == 0);
+
+        mFingers.put(id, new PointF(x, y));
+        int n = mFingers.size();
+
+        if (mCache.get(n) == null) {
+            PointerProperties[] properties = new PointerProperties[n];
+            PointerCoords[] coords = new PointerCoords[n];
+            for (int i = 0; i < n; i++) {
+                properties[i] = new PointerProperties();
+                coords[i] = new PointerCoords();
+            }
+            mCache.put(n, new Pair(properties, coords));
+        }
+
+        int action;
+        if (isInitialDown) {
+            action = MotionEvent.ACTION_DOWN;
+        } else {
+            action = MotionEvent.ACTION_POINTER_DOWN;
+            // Set the id of the changed pointer.
+            action |= index << MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+        }
+        generateEvent(action, ms, ev);
+        return this;
+    }
+
+    public TouchEventTranslator position(int id, float x, float y) {
+        checkFingerExistence(id, true);
+        mFingers.get(id).set(x, y);
+        return this;
+    }
+
+    private TouchEventTranslator lift(int id, int index, MotionEvent ev) {
+        checkFingerExistence(id, true);
+        boolean isFinalUp = (mFingers.size() == 1);
+        int action;
+        if (isFinalUp) {
+            action = MotionEvent.ACTION_UP;
+        } else {
+            action = MotionEvent.ACTION_POINTER_UP;
+            // Set the id of the changed pointer.
+            action |= index << MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+        }
+        generateEvent(action, ev);
+        mFingers.remove(id);
+        return this;
+    }
+
+    private TouchEventTranslator lift(int id, int index, float x, float y, MotionEvent ev) {
+        checkFingerExistence(id, true);
+        mFingers.get(id).set(x, y);
+        return lift(id, index, ev);
+    }
+
+    public TouchEventTranslator cancel(MotionEvent ev) {
+        generateEvent(MotionEvent.ACTION_CANCEL, ev);
+        mFingers.clear();
+        return this;
+    }
+
+    private void checkFingerExistence(int id, boolean shouldExist) {
+        if (shouldExist != (mFingers.get(id, null) != null)) {
+            throw new IllegalArgumentException(
+                    shouldExist ? "Finger does not exist" : "Finger already exists");
+        }
+    }
+
+
+    /**
+     * Used to debug MotionEvents being sent/received.
+     */
+    public void printSamples(String msg, MotionEvent ev) {
+        System.out.printf("%s %s", msg, MotionEvent.actionToString(ev.getActionMasked()));
+        final int pointerCount = ev.getPointerCount();
+        System.out.printf("#%d/%d", ev.getActionIndex(), pointerCount);
+        System.out.printf(" t=%d:", ev.getEventTime());
+        for (int p = 0; p < pointerCount; p++) {
+            System.out.printf("  id=%d: (%f,%f)",
+                    ev.getPointerId(p), ev.getX(p), ev.getY(p));
+        }
+        System.out.println();
+    }
+
+    private void generateEvent(int action, MotionEvent ev) {
+        generateEvent(action, ev.getEventTime(), ev);
+    }
+
+    private void generateEvent(int action, long ms, MotionEvent ev) {
+        Pair<PointerProperties[], PointerCoords[]> state = getFingerState();
+        MotionEvent event = MotionEvent.obtain(
+                mDownEvents.get(0).timeStamp,
+                ms,
+                action,
+                state.first.length,
+                state.first,
+                state.second,
+                ev.getMetaState(),
+                ev.getButtonState() /* buttonState */,
+                ev.getXPrecision() /* xPrecision */,
+                ev.getYPrecision() /* yPrecision */,
+                ev.getDeviceId(),
+                ev.getEdgeFlags(),
+                ev.getSource(),
+                ev.getFlags() /* flags */);
+        if (DEBUG) {
+            printSamples(TAG + " generateEvent", event);
+        }
+        if (event.getPointerId(event.getActionIndex()) < 0) {
+            printSamples(TAG + "generateEvent", event);
+            throw new IllegalStateException(event.getActionIndex() + " not found in MotionEvent");
+        }
+        mListener.accept(event);
+        event.recycle();
+    }
+
+    /**
+     * Returns the description of the fingers' state expected by MotionEvent.
+     */
+    private Pair<PointerProperties[], PointerCoords[]> getFingerState() {
+        int nFingers = mFingers.size();
+
+        Pair<PointerProperties[], PointerCoords[]> result = mCache.get(nFingers);
+        PointerProperties[] properties = result.first;
+        PointerCoords[] coordinates = result.second;
+
+        int index = 0;
+        for (int i = 0; i < mFingers.size(); i++) {
+            int id = mFingers.keyAt(i);
+            PointF location = mFingers.get(id);
+
+            PointerProperties property = properties[i];
+            property.id = id;
+            property.toolType = MotionEvent.TOOL_TYPE_FINGER;
+            properties[index] = property;
+
+            PointerCoords coordinate = coordinates[i];
+            coordinate.x = location.x;
+            coordinate.y = location.y;
+            coordinate.pressure = 1.0f;
+            coordinates[index] = coordinate;
+
+            index++;
+        }
+        return mCache.get(nFingers);
+    }
+}
diff --git a/src/com/android/launcher3/touch/WorkspaceTouchListener.java b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
index f59f14e..310d598 100644
--- a/src/com/android/launcher3/touch/WorkspaceTouchListener.java
+++ b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
@@ -17,18 +17,20 @@
 
 import static android.view.MotionEvent.ACTION_CANCEL;
 import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_MOVE;
 import static android.view.MotionEvent.ACTION_POINTER_UP;
 import static android.view.MotionEvent.ACTION_UP;
-import static android.view.ViewConfiguration.getLongPressTimeout;
 
 import static com.android.launcher3.LauncherState.NORMAL;
 
 import android.graphics.PointF;
 import android.graphics.Rect;
+import android.view.GestureDetector;
 import android.view.HapticFeedbackConstants;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.OnTouchListener;
+import android.view.ViewConfiguration;
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.CellLayout;
@@ -43,7 +45,8 @@
 /**
  * Helper class to handle touch on empty space in workspace and show options popup on long press
  */
-public class WorkspaceTouchListener implements OnTouchListener, Runnable {
+public class WorkspaceTouchListener extends GestureDetector.SimpleOnGestureListener
+        implements OnTouchListener {
 
     /**
      * STATE_PENDING_PARENT_INFORM is the state between longPress performed & the next motionEvent.
@@ -60,16 +63,25 @@
     private final Launcher mLauncher;
     private final Workspace mWorkspace;
     private final PointF mTouchDownPoint = new PointF();
+    private final float mTouchSlop;
 
     private int mLongPressState = STATE_CANCELLED;
 
+    private final GestureDetector mGestureDetector;
+
     public WorkspaceTouchListener(Launcher launcher, Workspace workspace) {
         mLauncher = launcher;
         mWorkspace = workspace;
+        // Use twice the touch slop as we are looking for long press which is more
+        // likely to cause movement.
+        mTouchSlop = 2 * ViewConfiguration.get(launcher).getScaledTouchSlop();
+        mGestureDetector = new GestureDetector(workspace.getContext(), this);
     }
 
     @Override
     public boolean onTouch(View view, MotionEvent ev) {
+        mGestureDetector.onTouchEvent(ev);
+
         int action = ev.getActionMasked();
         if (action == ACTION_DOWN) {
             // Check if we can handle long press.
@@ -87,11 +99,9 @@
                 handleLongPress = mTempRect.contains((int) ev.getX(), (int) ev.getY());
             }
 
-            cancelLongPress();
             if (handleLongPress) {
                 mLongPressState = STATE_REQUESTED;
                 mTouchDownPoint.set(ev.getX(), ev.getY());
-                mWorkspace.postDelayed(this, getLongPressTimeout());
             }
 
             mWorkspace.onTouchEvent(ev);
@@ -116,6 +126,9 @@
             mWorkspace.onTouchEvent(ev);
             if (mWorkspace.isHandlingTouch()) {
                 cancelLongPress();
+            } else if (action == ACTION_MOVE && PointF.length(
+                    mTouchDownPoint.x - ev.getX(), mTouchDownPoint.y - ev.getY()) > mTouchSlop) {
+                cancelLongPress();
             }
 
             result = true;
@@ -125,7 +138,7 @@
         }
 
         if (action == ACTION_UP || action == ACTION_POINTER_UP) {
-            if (!mWorkspace.isTouchActive()) {
+            if (!mWorkspace.isHandlingTouch()) {
                 final CellLayout currentPage =
                         (CellLayout) mWorkspace.getChildAt(mWorkspace.getCurrentPage());
                 if (currentPage != null) {
@@ -137,6 +150,7 @@
         if (action == ACTION_UP || action == ACTION_CANCEL) {
             cancelLongPress();
         }
+
         return result;
     }
 
@@ -146,12 +160,11 @@
     }
 
     private void cancelLongPress() {
-        mWorkspace.removeCallbacks(this);
         mLongPressState = STATE_CANCELLED;
     }
 
     @Override
-    public void run() {
+    public void onLongPress(MotionEvent event) {
         if (mLongPressState == STATE_REQUESTED) {
             if (canHandleLongPress()) {
                 mLongPressState = STATE_PENDING_PARENT_INFORM;
diff --git a/src/com/android/launcher3/util/ComponentKey.java b/src/com/android/launcher3/util/ComponentKey.java
deleted file mode 100644
index d478ffa..0000000
--- a/src/com/android/launcher3/util/ComponentKey.java
+++ /dev/null
@@ -1,58 +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 android.content.ComponentName;
-import android.os.UserHandle;
-
-import java.util.Arrays;
-
-public class ComponentKey {
-
-    public final ComponentName componentName;
-    public final UserHandle user;
-
-    private final int mHashCode;
-
-    public ComponentKey(ComponentName componentName, UserHandle user) {
-        Preconditions.assertNotNull(componentName);
-        Preconditions.assertNotNull(user);
-        this.componentName = componentName;
-        this.user = user;
-        mHashCode = Arrays.hashCode(new Object[] {componentName, user});
-
-    }
-
-    @Override
-    public int hashCode() {
-        return mHashCode;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        ComponentKey other = (ComponentKey) o;
-        return other.componentName.equals(componentName) && other.user.equals(user);
-    }
-
-    /**
-     * Encodes a component key as a string of the form [flattenedComponentString#userId].
-     */
-    @Override
-    public String toString() {
-        return componentName.flattenToString() + "#" + user;
-    }
-}
\ No newline at end of file
diff --git a/src/com/android/launcher3/util/ConfigMonitor.java b/src/com/android/launcher3/util/ConfigMonitor.java
index 611931d..12d35e9 100644
--- a/src/com/android/launcher3/util/ConfigMonitor.java
+++ b/src/com/android/launcher3/util/ConfigMonitor.java
@@ -29,9 +29,13 @@
 import android.view.Display;
 import android.view.WindowManager;
 
+import com.android.launcher3.MainThreadExecutor;
+
+import java.util.function.Consumer;
+
 /**
  * {@link BroadcastReceiver} which watches configuration changes and
- * restarts the process in case changes which affect the device profile occur.
+ * notifies the callback in case changes which affect the device profile occur.
  */
 public class ConfigMonitor extends BroadcastReceiver implements DisplayListener {
 
@@ -48,7 +52,9 @@
     private final Point mRealSize;
     private final Point mSmallestSize, mLargestSize;
 
-    public ConfigMonitor(Context context) {
+    private Consumer<Context> mCallback;
+
+    public ConfigMonitor(Context context, Consumer<Context> callback) {
         mContext = context;
 
         Configuration config = context.getResources().getConfiguration();
@@ -64,23 +70,26 @@
         mSmallestSize = new Point();
         mLargestSize = new Point();
         display.getCurrentSizeRange(mSmallestSize, mLargestSize);
+
+        mCallback = callback;
+
+        // Listen for configuration change
+        mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED));
+
+        // Listen for display manager change
+        mContext.getSystemService(DisplayManager.class)
+                .registerDisplayListener(this, new Handler(UiThreadHelper.getBackgroundLooper()));
     }
 
     @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");
-            killProcess();
+            Log.d(TAG, "Configuration changed.");
+            notifyChange();
         }
     }
 
-    public void register() {
-        mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED));
-        mContext.getSystemService(DisplayManager.class)
-                .registerDisplayListener(this, new Handler(UiThreadHelper.getBackgroundLooper()));
-    }
-
     @Override
     public void onDisplayAdded(int displayId) { }
 
@@ -97,7 +106,7 @@
 
         if (!mRealSize.equals(mTmpPoint1) && !mRealSize.equals(mTmpPoint1.y, mTmpPoint1.x)) {
             Log.d(TAG, String.format("Display size changed from %s to %s", mRealSize, mTmpPoint1));
-            killProcess();
+            notifyChange();
             return;
         }
 
@@ -105,18 +114,28 @@
         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));
-            killProcess();
+            notifyChange();
         }
     }
 
-    private void killProcess() {
-        Log.d(TAG, "restarting launcher");
-        mContext.unregisterReceiver(this);
-        mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this);
-        android.os.Process.killProcess(android.os.Process.myPid());
+    private synchronized void notifyChange() {
+        if (mCallback != null) {
+            Consumer<Context> callback = mCallback;
+            mCallback = null;
+            new MainThreadExecutor().execute(() -> callback.accept(mContext));
+        }
     }
 
     private Display getDefaultDisplay(Context context) {
         return context.getSystemService(WindowManager.class).getDefaultDisplay();
     }
+
+    public void unregister() {
+        try {
+            mContext.unregisterReceiver(this);
+            mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(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 4384328..00adf10 100644
--- a/src/com/android/launcher3/util/ContentWriter.java
+++ b/src/com/android/launcher3/util/ContentWriter.java
@@ -25,8 +25,8 @@
 
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.icons.GraphicsUtils;
 
 /**
  * A wrapper around {@link ContentValues} with some utility methods.
@@ -97,7 +97,7 @@
         Preconditions.assertNonUiThread();
         if (mIcon != null && !LauncherAppState.getInstance(context).getIconCache()
                 .isDefaultIcon(mIcon, mUser)) {
-            mValues.put(LauncherSettings.Favorites.ICON, Utilities.flattenBitmap(mIcon));
+            mValues.put(LauncherSettings.Favorites.ICON, GraphicsUtils.flattenBitmap(mIcon));
             mIcon = null;
         }
         return mValues;
diff --git a/src/com/android/launcher3/util/FlagOp.java b/src/com/android/launcher3/util/FlagOp.java
index 5e26ed1..bd40eb9 100644
--- a/src/com/android/launcher3/util/FlagOp.java
+++ b/src/com/android/launcher3/util/FlagOp.java
@@ -1,30 +1,16 @@
 package com.android.launcher3.util;
 
-public abstract class FlagOp {
+public interface FlagOp {
 
-    public static FlagOp NO_OP = new FlagOp() {};
+    FlagOp NO_OP = i -> i;
 
-    private FlagOp() {}
+    int apply(int flags);
 
-    public int apply(int flags) {
-        return flags;
+    static FlagOp addFlag(int flag) {
+        return i -> i | flag;
     }
 
-    public static FlagOp addFlag(final int flag) {
-        return new FlagOp() {
-            @Override
-            public int apply(int flags) {
-                return flags | flag;
-            }
-        };
-    }
-
-    public static FlagOp removeFlag(final int flag) {
-        return new FlagOp() {
-            @Override
-            public int apply(int flags) {
-                return flags & ~flag;
-            }
-        };
+    static FlagOp removeFlag(int flag) {
+        return i -> i & ~flag;
     }
 }
diff --git a/src/com/android/launcher3/util/FlingAnimation.java b/src/com/android/launcher3/util/FlingAnimation.java
index fe0571b..9d0ad22 100644
--- a/src/com/android/launcher3/util/FlingAnimation.java
+++ b/src/com/android/launcher3/util/FlingAnimation.java
@@ -14,6 +14,7 @@
 import com.android.launcher3.DropTarget.DragObject;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.dragndrop.DragView;
 
 public class FlingAnimation implements AnimatorUpdateListener, Runnable {
@@ -28,6 +29,7 @@
     private final Launcher mLauncher;
 
     protected final DragObject mDragObject;
+    protected final DragOptions mDragOptions;
     protected final DragLayer mDragLayer;
     protected final TimeInterpolator mAlphaInterpolator = new DecelerateInterpolator(0.75f);
     protected final float mUX, mUY;
@@ -39,13 +41,15 @@
 
     protected float mAX, mAY;
 
-    public FlingAnimation(DragObject d, PointF vel, ButtonDropTarget dropTarget, Launcher launcher) {
+    public FlingAnimation(DragObject d, PointF vel, ButtonDropTarget dropTarget, Launcher launcher,
+            DragOptions options) {
         mDropTarget = dropTarget;
         mLauncher = launcher;
         mDragObject = d;
         mUX = vel.x / 1000;
         mUY = vel.y / 1000;
         mDragLayer = mLauncher.getDragLayer();
+        mDragOptions = options;
     }
 
     @Override
@@ -102,6 +106,7 @@
             }
         };
 
+        mDropTarget.onDrop(mDragObject, mDragOptions);
         mDragLayer.animateView(mDragObject.dragView, this, duration, tInterpolator,
                 onAnimationEndRunnable, DragLayer.ANIMATION_END_DISAPPEAR, null);
     }
diff --git a/src/com/android/launcher3/util/FloatRange.java b/src/com/android/launcher3/util/FloatRange.java
deleted file mode 100644
index 12772f3..0000000
--- a/src/com/android/launcher3/util/FloatRange.java
+++ /dev/null
@@ -1,39 +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;
-
-/**
- * A mutable class for describing the range of two int values.
- */
-public class FloatRange {
-
-    public float start, end;
-
-    public FloatRange() { }
-
-    public FloatRange(float s, float e) {
-        set(s, e);
-    }
-
-    public void set(float s, float e) {
-        start = s;
-        end = e;
-    }
-
-    public boolean contains(float value) {
-        return value >= start && value <= end;
-    }
-}
diff --git a/src/com/android/launcher3/util/FocusLogic.java b/src/com/android/launcher3/util/FocusLogic.java
index b793f54..4f4cccd 100644
--- a/src/com/android/launcher3/util/FocusLogic.java
+++ b/src/com/android/launcher3/util/FocusLogic.java
@@ -201,10 +201,6 @@
         ViewGroup hotseatParent = hotseatLayout.getShortcutsAndWidgets();
 
         boolean isHotseatHorizontal = !dp.isVerticalBarLayout();
-        boolean moreIconsInHotseatThanWorkspace = !FeatureFlags.NO_ALL_APPS_ICON &&
-                (isHotseatHorizontal
-                        ? hotseatLayout.getCountX() > iconLayout.getCountX()
-                        : hotseatLayout.getCountY() > iconLayout.getCountY());
 
         int m, n;
         if (isHotseatHorizontal) {
@@ -215,19 +211,7 @@
             n = hotseatLayout.getCountY();
         }
         int[][] matrix = createFullMatrix(m, n);
-        if (moreIconsInHotseatThanWorkspace) {
-            int allappsiconRank = dp.inv.getAllAppsButtonRank();
-            if (isHotseatHorizontal) {
-                for (int j = 0; j < n; j++) {
-                    matrix[allappsiconRank][j] = ALL_APPS_COLUMN;
-                }
-            } else {
-                for (int j = 0; j < m; j++) {
-                    matrix[j][allappsiconRank] = ALL_APPS_COLUMN;
-                }
-            }
-        }
-        // Iterate thru the children of the workspace.
+        // Iterate through the children of the workspace.
         for (int i = 0; i < iconParent.getChildCount(); i++) {
             View cell = iconParent.getChildAt(i);
             if (!cell.isFocusable()) {
@@ -235,17 +219,6 @@
             }
             int cx = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellX;
             int cy = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellY;
-            if (moreIconsInHotseatThanWorkspace) {
-                int allappsiconRank = dp.inv.getAllAppsButtonRank();
-                if (isHotseatHorizontal && cx >= allappsiconRank) {
-                    // Add 1 to account for the All Apps button.
-                    cx++;
-                }
-                if (!isHotseatHorizontal && cy >= allappsiconRank) {
-                    // Add 1 to account for the All Apps button.
-                    cy++;
-                }
-            }
             matrix[cx][cy] = i;
         }
 
diff --git a/src/com/android/launcher3/util/IOUtils.java b/src/com/android/launcher3/util/IOUtils.java
index 77c21fe..f95f74d 100644
--- a/src/com/android/launcher3/util/IOUtils.java
+++ b/src/com/android/launcher3/util/IOUtils.java
@@ -16,12 +16,18 @@
 
 package com.android.launcher3.util;
 
+import android.content.Context;
+
+import com.android.launcher3.config.FeatureFlags;
+
 import java.io.ByteArrayOutputStream;
 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,4 +58,23 @@
         }
         return total;
     }
+
+    /**
+     * Utility method to debug binary data
+     */
+    public static String createTempFile(Context context, byte[] data) {
+        if (!FeatureFlags.IS_DOGFOOD_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();
+    }
 }
diff --git a/src/com/android/launcher3/util/InstantAppResolver.java b/src/com/android/launcher3/util/InstantAppResolver.java
index 4485427..031a40d 100644
--- a/src/com/android/launcher3/util/InstantAppResolver.java
+++ b/src/com/android/launcher3/util/InstantAppResolver.java
@@ -23,18 +23,14 @@
 
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-
-import java.util.Collections;
-import java.util.List;
 
 /**
  * A wrapper class to access instant app related APIs.
  */
-public class InstantAppResolver {
+public class InstantAppResolver implements ResourceBasedOverride {
 
     public static InstantAppResolver newInstance(Context context) {
-        return Utilities.getOverrideObject(
+        return Overrides.getObject(
                 InstantAppResolver.class, context, R.string.instant_app_resolver_class);
     }
 
@@ -56,8 +52,4 @@
         }
         return false;
     }
-
-    public List<ApplicationInfo> getInstantApps() {
-        return Collections.emptyList();
-    }
 }
diff --git a/src/com/android/launcher3/util/IntArray.java b/src/com/android/launcher3/util/IntArray.java
new file mode 100644
index 0000000..d2a551f
--- /dev/null
+++ b/src/com/android/launcher3/util/IntArray.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+import java.util.Arrays;
+
+/**
+ * Copy of the platform hidden implementation of android.util.IntArray.
+ * Implements a growing array of int primitives.
+ */
+public class IntArray implements Cloneable {
+    private static final int MIN_CAPACITY_INCREMENT = 12;
+
+    private static final int[] EMPTY_INT = new int[0];
+
+    /* package private */ int[] mValues;
+    /* package private */ int mSize;
+
+    private  IntArray(int[] array, int size) {
+        mValues = array;
+        mSize = size;
+    }
+
+    /**
+     * Creates an empty IntArray with the default initial capacity.
+     */
+    public IntArray() {
+        this(10);
+    }
+
+    /**
+     * Creates an empty IntArray with the specified initial capacity.
+     */
+    public IntArray(int initialCapacity) {
+        if (initialCapacity == 0) {
+            mValues = EMPTY_INT;
+        } else {
+            mValues = new int[initialCapacity];
+        }
+        mSize = 0;
+    }
+
+    /**
+     * Creates an IntArray wrapping the given primitive int array.
+     */
+    public static IntArray wrap(int... array) {
+        return new IntArray(array, array.length);
+    }
+
+    /**
+     * Appends the specified value to the end of this array.
+     */
+    public void add(int value) {
+        add(mSize, value);
+    }
+
+    /**
+     * Inserts a value at the specified position in this array. If the specified index is equal to
+     * the length of the array, the value is added at the end.
+     *
+     * @throws IndexOutOfBoundsException when index &lt; 0 || index &gt; size()
+     */
+    public void add(int index, int value) {
+        ensureCapacity(1);
+        int rightSegment = mSize - index;
+        mSize++;
+        checkBounds(mSize, index);
+
+        if (rightSegment != 0) {
+            // Move by 1 all values from the right of 'index'
+            System.arraycopy(mValues, index, mValues, index + 1, rightSegment);
+        }
+
+        mValues[index] = value;
+    }
+
+    /**
+     * Adds the values in the specified array to this array.
+     */
+    public void addAll(IntArray values) {
+        final int count = values.mSize;
+        ensureCapacity(count);
+
+        System.arraycopy(values.mValues, 0, mValues, mSize, count);
+        mSize += count;
+    }
+
+    /**
+     * Sets the array to be same as {@param other}
+     */
+    public void copyFrom(IntArray other) {
+        clear();
+        addAll(other);
+    }
+
+    /**
+     * Ensures capacity to append at least <code>count</code> values.
+     */
+    private void ensureCapacity(int count) {
+        final int currentSize = mSize;
+        final int minCapacity = currentSize + count;
+        if (minCapacity >= mValues.length) {
+            final int targetCap = currentSize + (currentSize < (MIN_CAPACITY_INCREMENT / 2) ?
+                    MIN_CAPACITY_INCREMENT : currentSize >> 1);
+            final int newCapacity = targetCap > minCapacity ? targetCap : minCapacity;
+            final int[] newValues = new int[newCapacity];
+            System.arraycopy(mValues, 0, newValues, 0, currentSize);
+            mValues = newValues;
+        }
+    }
+
+    /**
+     * Removes all values from this array.
+     */
+    public void clear() {
+        mSize = 0;
+    }
+
+    @Override
+    public IntArray clone() {
+        return wrap(toArray());
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (obj instanceof IntArray) {
+            IntArray arr = (IntArray) obj;
+            if (mSize == arr.mSize) {
+                for (int i = 0; i < mSize; i++) {
+                    if (arr.mValues[i] != mValues[i]) {
+                        return false;
+                    }
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns the value at the specified position in this array.
+     */
+    public int get(int index) {
+        checkBounds(mSize, index);
+        return mValues[index];
+    }
+
+    /**
+     * Sets the value at the specified position in this array.
+     */
+    public void set(int index, int value) {
+        checkBounds(mSize, index);
+        mValues[index] = value;
+    }
+
+    /**
+     * Returns the index of the first occurrence of the specified value in this
+     * array, or -1 if this array does not contain the value.
+     */
+    public int indexOf(int value) {
+        final int n = mSize;
+        for (int i = 0; i < n; i++) {
+            if (mValues[i] == value) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    public boolean contains(int value) {
+        return indexOf(value) >= 0;
+    }
+
+    public boolean isEmpty() {
+        return mSize == 0;
+    }
+
+    /**
+     * Removes the value at the specified index from this array.
+     */
+    public void removeIndex(int index) {
+        checkBounds(mSize, index);
+        System.arraycopy(mValues, index + 1, mValues, index, mSize - index - 1);
+        mSize--;
+    }
+
+    /**
+     * Removes the values if it exists
+     */
+    public void removeValue(int value) {
+        int index = indexOf(value);
+        if (index >= 0) {
+            removeIndex(index);
+        }
+    }
+
+    /**
+     * Removes the values if it exists
+     */
+    public void removeAllValues(IntArray values) {
+        for (int i = 0; i < values.mSize; i++) {
+            removeValue(values.mValues[i]);
+        }
+    }
+
+    /**
+     * Returns the number of values in this array.
+     */
+    public int size() {
+        return mSize;
+    }
+
+    /**
+     * Returns a new array with the contents of this IntArray.
+     */
+    public int[] toArray() {
+        return mSize == 0 ? EMPTY_INT : Arrays.copyOf(mValues, mSize);
+    }
+
+    /**
+     * Returns a comma separate list of all values.
+     */
+    public String toConcatString() {
+        StringBuilder b = new StringBuilder();
+        for (int i = 0; i < mSize ; i++) {
+            if (i > 0) {
+                b.append(", ");
+            }
+            b.append(mValues[i]);
+        }
+        return b.toString();
+    }
+
+    /**
+     * Throws {@link ArrayIndexOutOfBoundsException} if the index is out of bounds.
+     *
+     * @param len length of the array. Must be non-negative
+     * @param index the index to check
+     * @throws ArrayIndexOutOfBoundsException if the {@code index} is out of bounds of the array
+     */
+    private static void checkBounds(int len, int index) {
+        if (index < 0 || len <= index) {
+            throw new ArrayIndexOutOfBoundsException("length=" + len + "; index=" + index);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/util/IntSet.java b/src/com/android/launcher3/util/IntSet.java
new file mode 100644
index 0000000..851f129
--- /dev/null
+++ b/src/com/android/launcher3/util/IntSet.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util;
+
+import java.util.Arrays;
+
+/**
+ * A wrapper over IntArray implementing a growing set of int primitives.
+ */
+public class IntSet {
+
+    final IntArray mArray = new IntArray();
+
+    /**
+     * Appends the specified value to the set if it does not exist.
+     */
+    public void add(int value) {
+        int index = Arrays.binarySearch(mArray.mValues, 0, mArray.mSize, value);
+        if (index < 0) {
+            mArray.add(-index - 1, value);
+        }
+    }
+
+    public boolean contains(int value) {
+        return Arrays.binarySearch(mArray.mValues, 0, mArray.mSize, value) >= 0;
+    }
+
+    public boolean isEmpty() {
+        return mArray.isEmpty();
+    }
+
+    /**
+     * Returns the number of values in this set.
+     */
+    public int size() {
+        return mArray.size();
+    }
+
+    public void clear() {
+        mArray.clear();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        return (obj instanceof IntSet) && ((IntSet) obj).mArray.equals(mArray);
+    }
+
+    public IntArray getArray() {
+        return mArray;
+    }
+
+    /**
+     * Sets this set to be same as {@param other}
+     */
+    public void copyFrom(IntSet other) {
+        mArray.copyFrom(other.mArray);
+    }
+
+    public static IntSet wrap(IntArray array) {
+        IntSet set = new IntSet();
+        set.mArray.addAll(array);
+        Arrays.sort(set.mArray.mValues, 0, set.mArray.mSize);
+        return set;
+    }
+}
diff --git a/src/com/android/launcher3/util/IntSparseArrayMap.java b/src/com/android/launcher3/util/IntSparseArrayMap.java
new file mode 100644
index 0000000..9d5391b
--- /dev/null
+++ b/src/com/android/launcher3/util/IntSparseArrayMap.java
@@ -0,0 +1,65 @@
+/*
+ * 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.SparseArray;
+
+import java.util.Iterator;
+
+/**
+ * Extension of {@link SparseArray} with some utility methods.
+ */
+public class IntSparseArrayMap<E> extends SparseArray<E> implements Iterable<E> {
+
+    public boolean containsKey(int key) {
+        return indexOfKey(key) >= 0;
+    }
+
+    public boolean isEmpty() {
+        return size() <= 0;
+    }
+
+    @Override
+    public IntSparseArrayMap<E> clone() {
+        return (IntSparseArrayMap<E>) super.clone();
+    }
+
+    @Override
+    public Iterator<E> iterator() {
+        return new ValueIterator();
+    }
+
+    @Thunk class ValueIterator implements Iterator<E> {
+
+        private int mNextIndex = 0;
+
+        @Override
+        public boolean hasNext() {
+            return mNextIndex < size();
+        }
+
+        @Override
+        public E next() {
+            return valueAt(mNextIndex ++);
+        }
+
+        @Override
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+    }
+}
diff --git a/src/com/android/launcher3/util/ItemInfoMatcher.java b/src/com/android/launcher3/util/ItemInfoMatcher.java
index daedaef..59a5ed6 100644
--- a/src/com/android/launcher3/util/ItemInfoMatcher.java
+++ b/src/com/android/launcher3/util/ItemInfoMatcher.java
@@ -18,13 +18,12 @@
 
 import android.content.ComponentName;
 import android.os.UserHandle;
-import android.util.SparseLongArray;
 
 import com.android.launcher3.FolderInfo;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.shortcuts.ShortcutKey;
 
 import java.util.HashSet;
@@ -32,25 +31,25 @@
 /**
  * A utility class to check for {@link ItemInfo}
  */
-public abstract class ItemInfoMatcher {
+public interface ItemInfoMatcher {
 
-    public abstract boolean matches(ItemInfo info, ComponentName cn);
+    boolean matches(ItemInfo info, ComponentName cn);
 
     /**
      * Filters {@param infos} to those satisfying the {@link #matches(ItemInfo, ComponentName)}.
      */
-    public final HashSet<ItemInfo> filterItemInfos(Iterable<ItemInfo> infos) {
+    default HashSet<ItemInfo> filterItemInfos(Iterable<ItemInfo> infos) {
         HashSet<ItemInfo> filtered = new HashSet<>();
         for (ItemInfo i : infos) {
-            if (i instanceof ShortcutInfo) {
-                ShortcutInfo info = (ShortcutInfo) i;
+            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 (ShortcutInfo s : info.contents) {
+                for (WorkspaceItemInfo s : info.contents) {
                     ComponentName cn = s.getTargetComponent();
                     if (cn != null && matches(s, cn)) {
                         filtered.add(s);
@@ -70,88 +69,43 @@
     /**
      * Returns a new matcher with returns true if either this or {@param matcher} returns true.
      */
-    public ItemInfoMatcher or(final ItemInfoMatcher matcher) {
-       final ItemInfoMatcher that = this;
-        return new ItemInfoMatcher() {
-            @Override
-            public boolean matches(ItemInfo info, ComponentName cn) {
-                return that.matches(info, cn) || matcher.matches(info, cn);
-            }
-        };
+    default ItemInfoMatcher or(ItemInfoMatcher matcher) {
+        return (info, cn) -> matches(info, cn) || matcher.matches(info, cn);
     }
 
     /**
      * Returns a new matcher with returns true if both this and {@param matcher} returns true.
      */
-    public ItemInfoMatcher and(final ItemInfoMatcher matcher) {
-        final ItemInfoMatcher that = this;
-        return new ItemInfoMatcher() {
-            @Override
-            public boolean matches(ItemInfo info, ComponentName cn) {
-                return that.matches(info, cn) && matcher.matches(info, cn);
-            }
-        };
+    default ItemInfoMatcher and(ItemInfoMatcher matcher) {
+        return (info, cn) -> matches(info, cn) && matcher.matches(info, cn);
     }
 
     /**
      * Returns a new matcher which returns the opposite boolean value of the provided
      * {@param matcher}.
      */
-    public static ItemInfoMatcher not(final ItemInfoMatcher matcher) {
-        return new ItemInfoMatcher() {
-            @Override
-            public boolean matches(ItemInfo info, ComponentName cn) {
-                return !matcher.matches(info, cn);
-            }
-        };
+    static ItemInfoMatcher not(ItemInfoMatcher matcher) {
+        return (info, cn) -> !matcher.matches(info, cn);
     }
 
-    public static ItemInfoMatcher ofUser(final UserHandle user) {
-        return new ItemInfoMatcher() {
-            @Override
-            public boolean matches(ItemInfo info, ComponentName cn) {
-                return info.user.equals(user);
-            }
-        };
+    static ItemInfoMatcher ofUser(UserHandle user) {
+        return (info, cn) -> info.user.equals(user);
     }
 
-    public static ItemInfoMatcher ofComponents(
-            final HashSet<ComponentName> components, final UserHandle user) {
-        return new ItemInfoMatcher() {
-            @Override
-            public boolean matches(ItemInfo info, ComponentName cn) {
-                return components.contains(cn) && info.user.equals(user);
-            }
-        };
+    static ItemInfoMatcher ofComponents(HashSet<ComponentName> components, UserHandle user) {
+        return (info, cn) -> components.contains(cn) && info.user.equals(user);
     }
 
-    public static ItemInfoMatcher ofPackages(
-            final HashSet<String> packageNames, final UserHandle user) {
-        return new ItemInfoMatcher() {
-            @Override
-            public boolean matches(ItemInfo info, ComponentName cn) {
-                return packageNames.contains(cn.getPackageName()) && info.user.equals(user);
-            }
-        };
+    static ItemInfoMatcher ofPackages(HashSet<String> packageNames, UserHandle user) {
+        return (info, cn) -> packageNames.contains(cn.getPackageName()) && info.user.equals(user);
     }
 
-    public static ItemInfoMatcher ofShortcutKeys(final HashSet<ShortcutKey> keys) {
-        return new ItemInfoMatcher() {
-            @Override
-            public boolean matches(ItemInfo info, ComponentName cn) {
-                return info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT &&
+    static ItemInfoMatcher ofShortcutKeys(HashSet<ShortcutKey> keys) {
+        return  (info, cn) -> info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT &&
                         keys.contains(ShortcutKey.fromItemInfo(info));
-            }
-        };
     }
 
-    public static ItemInfoMatcher ofItemIds(
-            final LongArrayMap<Boolean> ids, final Boolean matchDefault) {
-        return new ItemInfoMatcher() {
-            @Override
-            public boolean matches(ItemInfo info, ComponentName cn) {
-                return ids.get(info.id, matchDefault);
-            }
-        };
+    static ItemInfoMatcher ofItemIds(IntSparseArrayMap<Boolean> ids, Boolean matchDefault) {
+        return (info, cn) -> ids.get(info.id, matchDefault);
     }
 }
diff --git a/src/com/android/launcher3/util/ListViewHighlighter.java b/src/com/android/launcher3/util/ListViewHighlighter.java
deleted file mode 100644
index ecad2af..0000000
--- a/src/com/android/launcher3/util/ListViewHighlighter.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.util;
-
-import android.animation.ArgbEvaluator;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.support.v4.graphics.ColorUtils;
-import android.view.View;
-import android.view.View.OnLayoutChangeListener;
-import android.widget.AbsListView;
-import android.widget.AbsListView.OnScrollListener;
-import android.widget.AbsListView.RecyclerListener;
-import android.widget.ListView;
-
-import com.android.launcher3.R;
-
-/**
- * Utility class to scroll and highlight a list view item
- */
-public class ListViewHighlighter implements OnScrollListener, RecyclerListener,
-        OnLayoutChangeListener {
-
-    private final ListView mListView;
-    private int mPosHighlight;
-
-    private boolean mColorAnimated = false;
-
-    public ListViewHighlighter(ListView listView, int posHighlight) {
-        mListView = listView;
-        mPosHighlight = posHighlight;
-        mListView.setOnScrollListener(this);
-        mListView.setRecyclerListener(this);
-        mListView.addOnLayoutChangeListener(this);
-
-        mListView.post(this::tryHighlight);
-    }
-
-    @Override
-    public void onLayoutChange(View v, int left, int top, int right, int bottom,
-            int oldLeft, int oldTop, int oldRight, int oldBottom) {
-        mListView.post(this::tryHighlight);
-    }
-
-    private void tryHighlight() {
-        if (mPosHighlight < 0 || mListView.getChildCount() == 0) {
-            return;
-        }
-        if (!highlightIfVisible(mListView.getFirstVisiblePosition(),
-                mListView.getLastVisiblePosition())) {
-            mListView.smoothScrollToPosition(mPosHighlight);
-        }
-    }
-
-    @Override
-    public void onScrollStateChanged(AbsListView absListView, int i) { }
-
-    @Override
-    public void onScroll(AbsListView view, int firstVisibleItem,
-            int visibleItemCount, int totalItemCount) {
-        highlightIfVisible(firstVisibleItem, firstVisibleItem + visibleItemCount);
-    }
-
-    private boolean highlightIfVisible(int start, int end) {
-        if (mPosHighlight < 0 || mListView.getChildCount() == 0) {
-            return false;
-        }
-        if (start > mPosHighlight || mPosHighlight > end) {
-            return false;
-        }
-        highlightView(mListView.getChildAt(mPosHighlight - start));
-
-        // finish highlight
-        mListView.setOnScrollListener(null);
-        mListView.removeOnLayoutChangeListener(this);
-
-        mPosHighlight = -1;
-        return true;
-    }
-
-    @Override
-    public void onMovedToScrapHeap(View view) {
-        unhighlightView(view);
-    }
-
-    private void highlightView(View view) {
-        if (Boolean.TRUE.equals(view.getTag(R.id.view_highlighted))) {
-            // already highlighted
-        } else {
-            view.setTag(R.id.view_highlighted, true);
-            view.setTag(R.id.view_unhighlight_background, view.getBackground());
-            view.setBackground(getHighlightBackground());
-            view.postDelayed(() -> {
-                unhighlightView(view);
-            }, 15000L);
-        }
-    }
-
-    private void unhighlightView(View view) {
-        if (Boolean.TRUE.equals(view.getTag(R.id.view_highlighted))) {
-            Object background = view.getTag(R.id.view_unhighlight_background);
-            if (background instanceof Drawable) {
-                view.setBackground((Drawable) background);
-            }
-            view.setTag(R.id.view_unhighlight_background, null);
-            view.setTag(R.id.view_highlighted, false);
-        }
-    }
-
-    private ColorDrawable getHighlightBackground() {
-        int color = ColorUtils.setAlphaComponent(Themes.getColorAccent(mListView.getContext()), 26);
-        if (mColorAnimated) {
-            return new ColorDrawable(color);
-        }
-        mColorAnimated = true;
-        ColorDrawable bg = new ColorDrawable(Color.WHITE);
-        ObjectAnimator anim = ObjectAnimator.ofInt(bg, "color", Color.WHITE, color);
-        anim.setEvaluator(new ArgbEvaluator());
-        anim.setDuration(200L);
-        anim.setRepeatMode(ValueAnimator.REVERSE);
-        anim.setRepeatCount(4);
-        anim.start();
-        return bg;
-    }
-}
diff --git a/src/com/android/launcher3/util/LongArrayMap.java b/src/com/android/launcher3/util/LongArrayMap.java
deleted file mode 100644
index a337e85..0000000
--- a/src/com/android/launcher3/util/LongArrayMap.java
+++ /dev/null
@@ -1,65 +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.LongSparseArray;
-
-import java.util.Iterator;
-
-/**
- * Extension of {@link LongSparseArray} with some utility methods.
- */
-public class LongArrayMap<E> extends LongSparseArray<E> implements Iterable<E> {
-
-    public boolean containsKey(long key) {
-        return indexOfKey(key) >= 0;
-    }
-
-    public boolean isEmpty() {
-        return size() <= 0;
-    }
-
-    @Override
-    public LongArrayMap<E> clone() {
-        return (LongArrayMap<E>) super.clone();
-    }
-
-    @Override
-    public Iterator<E> iterator() {
-        return new ValueIterator();
-    }
-
-    @Thunk class ValueIterator implements Iterator<E> {
-
-        private int mNextIndex = 0;
-
-        @Override
-        public boolean hasNext() {
-            return mNextIndex < size();
-        }
-
-        @Override
-        public E next() {
-            return valueAt(mNextIndex ++);
-        }
-
-        @Override
-        public void remove() {
-            throw new UnsupportedOperationException();
-        }
-    }
-}
diff --git a/src/com/android/launcher3/util/LooperIdleLock.java b/src/com/android/launcher3/util/LooperIdleLock.java
index 35cac14..2896535 100644
--- a/src/com/android/launcher3/util/LooperIdleLock.java
+++ b/src/com/android/launcher3/util/LooperIdleLock.java
@@ -19,8 +19,6 @@
 import android.os.Looper;
 import android.os.MessageQueue;
 
-import com.android.launcher3.Utilities;
-
 /**
  * Utility class to block execution until the UI looper is idle.
  */
@@ -33,13 +31,7 @@
     public LooperIdleLock(Object lock, Looper looper) {
         mLock = lock;
         mIsLocked = true;
-        if (Utilities.ATLEAST_MARSHMALLOW) {
-            looper.getQueue().addIdleHandler(this);
-        } else {
-            // Looper.myQueue() only gives the current queue. Move the execution to the UI thread
-            // so that the IdleHandler is attached to the correct message queue.
-            new LooperExecutor(looper).execute(this);
-        }
+        looper.getQueue().addIdleHandler(this);
     }
 
     @Override
diff --git a/src/com/android/launcher3/util/MainThreadInitializedObject.java b/src/com/android/launcher3/util/MainThreadInitializedObject.java
new file mode 100644
index 0000000..2ee0328
--- /dev/null
+++ b/src/com/android/launcher3/util/MainThreadInitializedObject.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util;
+
+import android.content.Context;
+import android.os.Looper;
+
+import com.android.launcher3.MainThreadExecutor;
+
+import java.util.concurrent.ExecutionException;
+
+import androidx.annotation.VisibleForTesting;
+
+/**
+ * Utility class for defining singletons which are initiated on main thread.
+ */
+public class MainThreadInitializedObject<T> {
+
+    private final ObjectProvider<T> mProvider;
+    private T mValue;
+
+    public MainThreadInitializedObject(ObjectProvider<T> provider) {
+        mProvider = provider;
+    }
+
+    public T get(Context context) {
+        if (mValue == null) {
+            if (Looper.myLooper() == Looper.getMainLooper()) {
+                mValue = mProvider.get(context.getApplicationContext());
+            } else {
+                try {
+                    return new MainThreadExecutor().submit(() -> get(context)).get();
+                } catch (InterruptedException|ExecutionException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        }
+        return mValue;
+    }
+
+    public T getNoCreate() {
+        return mValue;
+    }
+
+    @VisibleForTesting
+    public void initializeForTesting(T value) {
+        mValue = value;
+    }
+
+    public interface ObjectProvider<T> {
+
+        T get(Context context);
+    }
+}
diff --git a/src/com/android/launcher3/util/MultiValueAlpha.java b/src/com/android/launcher3/util/MultiValueAlpha.java
index f810f48..07f835d 100644
--- a/src/com/android/launcher3/util/MultiValueAlpha.java
+++ b/src/com/android/launcher3/util/MultiValueAlpha.java
@@ -19,6 +19,8 @@
 import android.util.Property;
 import android.view.View;
 
+import java.util.Arrays;
+
 /**
  * Utility class to handle separating a single value as a factor of multiple values
  */
@@ -55,6 +57,11 @@
         }
     }
 
+    @Override
+    public String toString() {
+        return Arrays.toString(mMyProperties);
+    }
+
     public AlphaProperty getProperty(int index) {
         return mMyProperties[index];
     }
@@ -97,5 +104,10 @@
         public float getValue() {
             return mValue;
         }
+
+        @Override
+        public String toString() {
+            return Float.toString(mValue);
+        }
     }
 }
diff --git a/src/com/android/launcher3/util/NoLocaleSQLiteHelper.java b/src/com/android/launcher3/util/NoLocaleSQLiteHelper.java
deleted file mode 100644
index 05a7d27..0000000
--- a/src/com/android/launcher3/util/NoLocaleSQLiteHelper.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.util;
-
-import static android.database.sqlite.SQLiteDatabase.NO_LOCALIZED_COLLATORS;
-
-import static com.android.launcher3.Utilities.ATLEAST_P;
-
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.database.DatabaseErrorHandler;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteDatabase.CursorFactory;
-import android.database.sqlite.SQLiteDatabase.OpenParams;
-import android.database.sqlite.SQLiteOpenHelper;
-
-/**
- * Extension of {@link SQLiteOpenHelper} which avoids creating default locale table by
- * A context wrapper which creates databases without support for localized collators.
- */
-public abstract class NoLocaleSQLiteHelper extends SQLiteOpenHelper {
-
-    public NoLocaleSQLiteHelper(Context context, String name, int version) {
-        super(ATLEAST_P ? context : new NoLocalContext(context), name, null, version);
-        if (ATLEAST_P) {
-            setOpenParams(new OpenParams.Builder().addOpenFlags(NO_LOCALIZED_COLLATORS).build());
-        }
-    }
-
-    private static class NoLocalContext extends ContextWrapper {
-        public NoLocalContext(Context base) {
-            super(base);
-        }
-
-        @Override
-        public SQLiteDatabase openOrCreateDatabase(
-                String name, int mode, CursorFactory factory, DatabaseErrorHandler errorHandler) {
-            return super.openOrCreateDatabase(
-                    name, mode | Context.MODE_NO_LOCALIZED_COLLATORS, factory, errorHandler);
-        }
-    }
-}
diff --git a/src/com/android/launcher3/util/OverScroller.java b/src/com/android/launcher3/util/OverScroller.java
new file mode 100644
index 0000000..fc8a138
--- /dev/null
+++ b/src/com/android/launcher3/util/OverScroller.java
@@ -0,0 +1,856 @@
+/*
+ * 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.DynamicAnimation;
+import androidx.dynamicanimation.animation.FloatPropertyCompat;
+import androidx.dynamicanimation.animation.SpringAnimation;
+import androidx.dynamicanimation.animation.SpringForce;
+
+/**
+ * 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.
+     *
+     * @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 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) {
+            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 (mState == SPRING) {
+                if (mSpring != null) {
+                    mSpring.cancel();
+                }
+                mSpring = new SpringAnimation(this, SPRING_PROPERTY);
+
+                mSpring.setSpring(new SpringForce(mFinal)
+                        .setStiffness(SpringForce.STIFFNESS_LOW)
+                        .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
+                mSpring.setStartVelocity(velocity);
+                mSpring.animateToFinalPosition(mFinal);
+                mSpring.addEndListener((animation, canceled, value, velocity1) -> {
+                    finish();
+                    mState = SPLINE;
+                    mSpring = null;
+                });
+            }
+            // 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/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index 0b3b632..7d3a941 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -21,6 +21,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.PackageManager;
@@ -30,6 +31,7 @@
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.PatternMatcher;
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
@@ -37,13 +39,11 @@
 
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.ItemInfo;
-import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.PendingAddItemInfo;
 import com.android.launcher3.PromiseAppInfo;
 import com.android.launcher3.R;
-import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.Utilities;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.compat.LauncherAppsCompat;
 
 import java.net.URISyntaxException;
@@ -100,14 +100,7 @@
      * {@link android.app.admin.DevicePolicyManager#isPackageSuspended}.
      */
     public static boolean isAppSuspended(ApplicationInfo info) {
-        // The value of FLAG_SUSPENDED was reused by a hidden constant
-        // ApplicationInfo.FLAG_PRIVILEGED prior to N, so only check for suspended flag on N
-        // or later.
-        if (Utilities.ATLEAST_NOUGAT) {
-            return (info.flags & ApplicationInfo.FLAG_SUSPENDED) != 0;
-        } else {
-            return false;
-        }
+        return (info.flags & ApplicationInfo.FLAG_SUSPENDED) != 0;
     }
 
     /**
@@ -136,11 +129,6 @@
             return false;
         }
 
-        if (!Utilities.ATLEAST_MARSHMALLOW) {
-            // These checks are sufficient for below M devices.
-            return true;
-        }
-
         // On M and above also check AppOpsManager for compatibility mode permissions.
         if (TextUtils.isEmpty(AppOpsManager.permissionToOp(target.activityInfo.permission))) {
             // There is no app-op for this permission, which could have been disabled.
@@ -184,6 +172,11 @@
         }
     }
 
+    public static Intent getStyleWallpapersIntent(Context context) {
+        return new Intent(Intent.ACTION_SET_WALLPAPER).setComponent(
+                new ComponentName(context.getString(R.string.wallpaper_picker_package),
+                "com.android.customization.picker.CustomizationPickerActivity"));
+    }
 
     /**
      * Starts the details activity for {@code info}
@@ -197,7 +190,7 @@
         ComponentName componentName = null;
         if (info instanceof AppInfo) {
             componentName = ((AppInfo) info).componentName;
-        } else if (info instanceof ShortcutInfo) {
+        } else if (info instanceof WorkspaceItemInfo) {
             componentName = info.getTargetComponent();
         } else if (info instanceof PendingAddItemInfo) {
             componentName = ((PendingAddItemInfo) info).componentName;
@@ -214,4 +207,17 @@
             }
         }
     }
+
+    /**
+     * Creates an intent filter to listen for actions with a specific package in the data field.
+     */
+    public static IntentFilter getPackageFilter(String pkg, String... actions) {
+        IntentFilter packageFilter = new IntentFilter();
+        for (String action : actions) {
+            packageFilter.addAction(action);
+        }
+        packageFilter.addDataScheme("package");
+        packageFilter.addDataSchemeSpecificPart(pkg, PatternMatcher.PATTERN_LITERAL);
+        return packageFilter;
+    }
 }
diff --git a/src/com/android/launcher3/util/Provider.java b/src/com/android/launcher3/util/Provider.java
deleted file mode 100644
index 1cdd8d6..0000000
--- a/src/com/android/launcher3/util/Provider.java
+++ /dev/null
@@ -1,38 +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;
-
-/**
- * Utility class to allow lazy initialization of objects.
- */
-public abstract class Provider<T> {
-
-    /**
-     * Initializes and returns the object. This may contain expensive operations not suitable
-     * to UI thread.
-     */
-    public abstract T get();
-
-    public static <T> Provider<T> of (final T value) {
-        return new Provider<T>() {
-            @Override
-            public T get() {
-                return value;
-            }
-        };
-    }
-}
diff --git a/src/com/android/launcher3/util/RaceConditionTracker.java b/src/com/android/launcher3/util/RaceConditionTracker.java
new file mode 100644
index 0000000..6954d0e
--- /dev/null
+++ b/src/com/android/launcher3/util/RaceConditionTracker.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+/**
+ * Event tracker for reliably reproducing race conditions in tests.
+ * The app should call onEvent() for events that the test will try to reproduce in all possible
+ * orders.
+ */
+public class RaceConditionTracker {
+    public final static boolean ENTER = true;
+    public final static boolean EXIT = false;
+    static final String ENTER_POSTFIX = "enter";
+    static final String EXIT_POSTFIX = "exit";
+
+    public interface EventProcessor {
+        void onEvent(String eventName);
+    }
+
+    private static EventProcessor sEventProcessor;
+
+    static void setEventProcessor(EventProcessor eventProcessor) {
+        sEventProcessor = eventProcessor;
+    }
+
+    public static void onEvent(String eventName) {
+        if (sEventProcessor != null) sEventProcessor.onEvent(eventName);
+    }
+
+    public static void onEvent(String eventName, boolean isEnter) {
+        if (sEventProcessor != null) {
+            sEventProcessor.onEvent(enterExitEvt(eventName, isEnter));
+        }
+    }
+
+    public static String enterExitEvt(String eventName, boolean isEnter) {
+        return eventName + ":" + (isEnter ? ENTER_POSTFIX : EXIT_POSTFIX);
+    }
+
+    public static String enterEvt(String eventName) {
+        return enterExitEvt(eventName, ENTER);
+    }
+
+    public static String exitEvt(String eventName) {
+        return enterExitEvt(eventName, EXIT);
+    }
+}
diff --git a/src/com/android/launcher3/util/ResourceBasedOverride.java b/src/com/android/launcher3/util/ResourceBasedOverride.java
new file mode 100644
index 0000000..e2c4992
--- /dev/null
+++ b/src/com/android/launcher3/util/ResourceBasedOverride.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * An interface to indicate that a class is dynamically loaded using resource overlay, hence its
+ * class name and constructor should be preserved by proguard
+ */
+public interface ResourceBasedOverride {
+
+    class Overrides {
+
+        private static final String TAG = "Overrides";
+
+        public static <T extends ResourceBasedOverride> T getObject(
+                Class<T> clazz, Context context, int resId) {
+            String className = context.getString(resId);
+            if (!TextUtils.isEmpty(className)) {
+                try {
+                    Class<?> cls = Class.forName(className);
+                    return (T) cls.getDeclaredConstructor(Context.class).newInstance(context);
+                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException
+                        | ClassCastException | NoSuchMethodException | InvocationTargetException e) {
+                    Log.e(TAG, "Bad overriden class", e);
+                }
+            }
+
+            try {
+                return clazz.newInstance();
+            } catch (InstantiationException|IllegalAccessException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+}
diff --git a/src/com/android/launcher3/util/SQLiteCacheHelper.java b/src/com/android/launcher3/util/SQLiteCacheHelper.java
deleted file mode 100644
index 44c1762..0000000
--- a/src/com/android/launcher3/util/SQLiteCacheHelper.java
+++ /dev/null
@@ -1,125 +0,0 @@
-package com.android.launcher3.util;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteException;
-import android.database.sqlite.SQLiteFullException;
-import android.database.sqlite.SQLiteOpenHelper;
-import android.util.Log;
-
-import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
-
-/**
- * An extension of {@link SQLiteOpenHelper} with utility methods for a single table cache DB.
- * Any exception during write operations are ignored, and any version change causes a DB reset.
- */
-public abstract class SQLiteCacheHelper {
-    private static final String TAG = "SQLiteCacheHelper";
-
-    private static final boolean NO_ICON_CACHE = FeatureFlags.IS_DOGFOOD_BUILD &&
-            Utilities.isPropertyEnabled(LogConfig.MEMORY_ONLY_ICON_CACHE);
-
-    private final String mTableName;
-    private final MySQLiteOpenHelper mOpenHelper;
-
-    private boolean mIgnoreWrites;
-
-    public SQLiteCacheHelper(Context context, String name, int version, String tableName) {
-        if (NO_ICON_CACHE) {
-            name = null;
-        }
-        mTableName = tableName;
-        mOpenHelper = new MySQLiteOpenHelper(context, name, version);
-
-        mIgnoreWrites = false;
-    }
-
-    /**
-     * @see SQLiteDatabase#delete(String, String, String[])
-     */
-    public void delete(String whereClause, String[] whereArgs) {
-        if (mIgnoreWrites) {
-            return;
-        }
-        try {
-            mOpenHelper.getWritableDatabase().delete(mTableName, whereClause, whereArgs);
-        } catch (SQLiteFullException e) {
-            onDiskFull(e);
-        } catch (SQLiteException e) {
-            Log.d(TAG, "Ignoring sqlite exception", e);
-        }
-    }
-
-    /**
-     * @see SQLiteDatabase#insertWithOnConflict(String, String, ContentValues, int)
-     */
-    public void insertOrReplace(ContentValues values) {
-        if (mIgnoreWrites) {
-            return;
-        }
-        try {
-            mOpenHelper.getWritableDatabase().insertWithOnConflict(
-                    mTableName, null, values, SQLiteDatabase.CONFLICT_REPLACE);
-        } catch (SQLiteFullException e) {
-            onDiskFull(e);
-        } catch (SQLiteException e) {
-            Log.d(TAG, "Ignoring sqlite exception", e);
-        }
-    }
-
-    private void onDiskFull(SQLiteFullException e) {
-        Log.e(TAG, "Disk full, all write operations will be ignored", e);
-        mIgnoreWrites = true;
-    }
-
-    /**
-     * @see SQLiteDatabase#query(String, String[], String, String[], String, String, String)
-     */
-    public Cursor query(String[] columns, String selection, String[] selectionArgs) {
-        return mOpenHelper.getReadableDatabase().query(
-                mTableName, columns, selection, selectionArgs, null, null, null);
-    }
-
-    public void clear() {
-        mOpenHelper.clearDB(mOpenHelper.getWritableDatabase());
-    }
-
-    protected abstract void onCreateTable(SQLiteDatabase db);
-
-    /**
-     * A private inner class to prevent direct DB access.
-     */
-    private class MySQLiteOpenHelper extends NoLocaleSQLiteHelper {
-
-        public MySQLiteOpenHelper(Context context, String name, int version) {
-            super(context, name, version);
-        }
-
-        @Override
-        public void onCreate(SQLiteDatabase db) {
-            onCreateTable(db);
-        }
-
-        @Override
-        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
-            if (oldVersion != newVersion) {
-                clearDB(db);
-            }
-        }
-
-        @Override
-        public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
-            if (oldVersion != newVersion) {
-                clearDB(db);
-            }
-        }
-
-        private void clearDB(SQLiteDatabase db) {
-            db.execSQL("DROP TABLE IF EXISTS " + mTableName);
-            onCreate(db);
-        }
-    }
-}
diff --git a/src/com/android/launcher3/util/SecureSettingsObserver.java b/src/com/android/launcher3/util/SecureSettingsObserver.java
new file mode 100644
index 0000000..48aa02b
--- /dev/null
+++ b/src/com/android/launcher3/util/SecureSettingsObserver.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.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/SettingsObserver.java b/src/com/android/launcher3/util/SettingsObserver.java
deleted file mode 100644
index 6baa242..0000000
--- a/src/com/android/launcher3/util/SettingsObserver.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.util;
-
-import android.content.ContentResolver;
-import android.database.ContentObserver;
-import android.os.Handler;
-import android.provider.Settings;
-
-public interface SettingsObserver {
-
-    /**
-     * Registers the content observer to call {@link #onSettingChanged(boolean)} when any of the
-     * passed settings change. The value passed to onSettingChanged() is based on the key setting.
-     */
-    void register(String keySetting, String ... dependentSettings);
-    void unregister();
-    void onSettingChanged(boolean keySettingEnabled);
-
-
-    abstract class Secure extends ContentObserver implements SettingsObserver {
-        private ContentResolver mResolver;
-        private String mKeySetting;
-
-        public Secure(ContentResolver resolver) {
-            super(new Handler());
-            mResolver = resolver;
-        }
-
-        @Override
-        public void register(String keySetting, String ... dependentSettings) {
-            mKeySetting = keySetting;
-            mResolver.registerContentObserver(
-                    Settings.Secure.getUriFor(mKeySetting), false, this);
-            for (String setting : dependentSettings) {
-                mResolver.registerContentObserver(
-                        Settings.Secure.getUriFor(setting), false, this);
-            }
-            onChange(true);
-        }
-
-        @Override
-        public void unregister() {
-            mResolver.unregisterContentObserver(this);
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            super.onChange(selfChange);
-            onSettingChanged(Settings.Secure.getInt(mResolver, mKeySetting, 1) == 1);
-        }
-    }
-
-    abstract class System extends ContentObserver implements SettingsObserver {
-        private ContentResolver mResolver;
-        private String mKeySetting;
-
-        public System(ContentResolver resolver) {
-            super(new Handler());
-            mResolver = resolver;
-        }
-
-        @Override
-        public void register(String keySetting, String ... dependentSettings) {
-            mKeySetting = keySetting;
-            mResolver.registerContentObserver(
-                    Settings.System.getUriFor(mKeySetting), false, this);
-            for (String setting : dependentSettings) {
-                mResolver.registerContentObserver(
-                        Settings.System.getUriFor(setting), false, this);
-            }
-            onChange(true);
-        }
-
-        @Override
-        public void unregister() {
-            mResolver.unregisterContentObserver(this);
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            super.onChange(selfChange);
-            onSettingChanged(Settings.System.getInt(mResolver, mKeySetting, 1) == 1);
-        }
-    }
-}
diff --git a/src/com/android/launcher3/util/Themes.java b/src/com/android/launcher3/util/Themes.java
index 5f965a3..0d02715 100644
--- a/src/com/android/launcher3/util/Themes.java
+++ b/src/com/android/launcher3/util/Themes.java
@@ -17,16 +17,66 @@
 package com.android.launcher3.util;
 
 import android.content.Context;
+import android.content.res.Configuration;
 import android.content.res.TypedArray;
 import android.graphics.Color;
 import android.graphics.ColorMatrix;
 import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.SparseArray;
+import android.util.TypedValue;
+
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.uioverrides.WallpaperColorInfo;
 
 /**
  * Various utility methods associated with theming.
  */
 public class Themes {
 
+    public static int getActivityThemeRes(Context context) {
+        WallpaperColorInfo wallpaperColorInfo = WallpaperColorInfo.getInstance(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();
+        }
+
+        if (darkTheme) {
+            return wallpaperColorInfo.supportsDarkText() ?
+                    R.style.AppTheme_Dark_DarkText : wallpaperColorInfo.isMainColorDark() ?
+                            R.style.AppTheme_Dark_DarkMainColor : R.style.AppTheme_Dark;
+        } else {
+            return wallpaperColorInfo.supportsDarkText() ?
+                    R.style.AppTheme_DarkText : wallpaperColorInfo.isMainColorDark() ?
+                            R.style.AppTheme_DarkMainColor : R.style.AppTheme;
+        }
+    }
+
+    public static String getDefaultBodyFont(Context context) {
+        TypedArray ta = context.obtainStyledAttributes(android.R.style.TextAppearance_DeviceDefault,
+                new int[]{android.R.attr.fontFamily});
+        String value = ta.getString(0);
+        ta.recycle();
+        return value;
+    }
+
+    public static float getDialogCornerRadius(Context context) {
+        return getDimension(context, android.R.attr.dialogCornerRadius,
+                context.getResources().getDimension(R.dimen.default_dialog_corner_radius));
+    }
+
+    public static float getDimension(Context context, int attr, float defaultValue) {
+        TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
+        float value = ta.getDimension(0, defaultValue);
+        ta.recycle();
+        return value;
+    }
+
     public static int getColorAccent(Context context) {
         return getAttrColor(context, android.R.attr.colorAccent);
     }
@@ -104,4 +154,29 @@
         target.getArray()[14] = Color.blue(dstColor) - Color.blue(srcColor);
         target.getArray()[19] = Color.alpha(dstColor) - Color.alpha(srcColor);
     }
+
+    /**
+     * Creates a map for attribute-name to value for all the values in {@param attrs} which can be
+     * held in memory for later use.
+     */
+    public static SparseArray<TypedValue> createValueMap(Context context, AttributeSet attrSet,
+            IntArray keysToIgnore) {
+        int count = attrSet.getAttributeCount();
+        IntArray attrNameArray = new IntArray(count);
+        for (int i = 0; i < count; i++) {
+            attrNameArray.add(attrSet.getAttributeNameResource(i));
+        }
+        attrNameArray.removeAllValues(keysToIgnore);
+
+        int[] attrNames = attrNameArray.toArray();
+        SparseArray<TypedValue> result = new SparseArray<>(attrNames.length);
+        TypedArray ta = context.obtainStyledAttributes(attrSet, attrNames);
+        for (int i = 0; i < attrNames.length; i++) {
+            TypedValue tv = new TypedValue();
+            ta.getValue(i, tv);
+            result.put(attrNames[i], tv);
+        }
+
+        return result;
+    }
 }
diff --git a/src/com/android/launcher3/util/TouchController.java b/src/com/android/launcher3/util/TouchController.java
index 3cca215..fc1d819 100644
--- a/src/com/android/launcher3/util/TouchController.java
+++ b/src/com/android/launcher3/util/TouchController.java
@@ -18,6 +18,8 @@
 
 import android.view.MotionEvent;
 
+import java.io.PrintWriter;
+
 public interface TouchController {
 
     /**
@@ -29,4 +31,6 @@
      * Called when the draglayer receives a intercept touch event.
      */
     boolean onControllerInterceptTouchEvent(MotionEvent ev);
+
+    default void dump(String prefix, PrintWriter writer) { }
 }
diff --git a/src/com/android/launcher3/util/TraceHelper.java b/src/com/android/launcher3/util/TraceHelper.java
index 4aa2f37..c24bb67 100644
--- a/src/com/android/launcher3/util/TraceHelper.java
+++ b/src/com/android/launcher3/util/TraceHelper.java
@@ -27,47 +27,52 @@
 import com.android.launcher3.config.FeatureFlags;
 
 /**
- * A wrapper around {@link Trace} to allow easier proguarding for production builds.
+ * A wrapper around {@link Trace} with some utility information.
  *
  * To enable any tracing log, execute the following command:
+ * $ adb shell setprop log.tag.LAUNCHER_TRACE VERBOSE
  * $ adb shell setprop log.tag.TAGNAME VERBOSE
  */
 public class TraceHelper {
 
-    private static final boolean ENABLED = FeatureFlags.IS_DOGFOOD_BUILD;
+    private static final boolean ENABLED = isLoggable("LAUNCHER_TRACE", VERBOSE);
 
-    private static final boolean SYSTEM_TRACE = false;
+    private static final boolean SYSTEM_TRACE = ENABLED;
     private static final ArrayMap<String, MutableLong> sUpTimes = ENABLED ? new ArrayMap<>() : null;
 
     public static void beginSection(String sectionName) {
         if (ENABLED) {
-            MutableLong time = sUpTimes.get(sectionName);
-            if (time == null) {
-                time = new MutableLong(isLoggable(sectionName, VERBOSE) ? 0 : -1);
-                sUpTimes.put(sectionName, time);
-            }
-            if (time.value >= 0) {
-                if (SYSTEM_TRACE) {
-                    Trace.beginSection(sectionName);
+            synchronized (sUpTimes) {
+                MutableLong time = sUpTimes.get(sectionName);
+                if (time == null) {
+                    time = new MutableLong(isLoggable(sectionName, VERBOSE) ? 0 : -1);
+                    sUpTimes.put(sectionName, time);
                 }
-                time.value = SystemClock.uptimeMillis();
+                if (time.value >= 0) {
+                    if (SYSTEM_TRACE) {
+                        Trace.beginSection(sectionName);
+                    }
+                    time.value = SystemClock.uptimeMillis();
+                }
             }
         }
     }
 
     public static void partitionSection(String sectionName, String partition) {
         if (ENABLED) {
-            MutableLong time = sUpTimes.get(sectionName);
-            if (time != null && time.value >= 0) {
+            synchronized (sUpTimes) {
+                MutableLong time = sUpTimes.get(sectionName);
+                if (time != null && time.value >= 0) {
 
-                if (SYSTEM_TRACE) {
-                    Trace.endSection();
-                    Trace.beginSection(sectionName);
+                    if (SYSTEM_TRACE) {
+                        Trace.endSection();
+                        Trace.beginSection(sectionName);
+                    }
+
+                    long now = SystemClock.uptimeMillis();
+                    Log.d(sectionName, partition + " : " + (now - time.value));
+                    time.value = now;
                 }
-
-                long now = SystemClock.uptimeMillis();
-                Log.d(sectionName, partition + " : " + (now - time.value));
-                time.value = now;
             }
         }
     }
@@ -80,12 +85,14 @@
 
     public static void endSection(String sectionName, String msg) {
         if (ENABLED) {
-            MutableLong time = sUpTimes.get(sectionName);
-            if (time != null && time.value >= 0) {
-                if (SYSTEM_TRACE) {
-                    Trace.endSection();
+            synchronized (sUpTimes) {
+                MutableLong time = sUpTimes.get(sectionName);
+                if (time != null && time.value >= 0) {
+                    if (SYSTEM_TRACE) {
+                        Trace.endSection();
+                    }
+                    Log.d(sectionName, msg + " : " + (SystemClock.uptimeMillis() - time.value));
                 }
-                Log.d(sectionName, msg + " : " + (SystemClock.uptimeMillis() - time.value));
             }
         }
     }
diff --git a/src/com/android/launcher3/util/UiThreadHelper.java b/src/com/android/launcher3/util/UiThreadHelper.java
index 27140a1..cc442f9 100644
--- a/src/com/android/launcher3/util/UiThreadHelper.java
+++ b/src/com/android/launcher3/util/UiThreadHelper.java
@@ -15,6 +15,7 @@
  */
 package com.android.launcher3.util;
 
+import android.app.Activity;
 import android.content.Context;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -33,6 +34,8 @@
     private static Handler sHandler;
 
     private static final int MSG_HIDE_KEYBOARD = 1;
+    private static final int MSG_SET_ORIENTATION = 2;
+    private static final int MSG_RUN_COMMAND = 3;
 
     public static Looper getBackgroundLooper() {
         if (sHandlerThread == null) {
@@ -55,6 +58,15 @@
         Message.obtain(getHandler(context), MSG_HIDE_KEYBOARD, token).sendToTarget();
     }
 
+    public static void setOrientationAsync(Activity activity, int orientation) {
+        Message.obtain(getHandler(activity), MSG_SET_ORIENTATION, orientation, 0, activity)
+                .sendToTarget();
+    }
+
+    public static void runAsyncCommand(Context context, AsyncCommand command, int arg1, int arg2) {
+        Message.obtain(getHandler(context), MSG_RUN_COMMAND, arg1, arg2, command).sendToTarget();
+    }
+
     private static class UiCallbacks implements Handler.Callback {
 
         private final InputMethodManager mIMM;
@@ -69,8 +81,19 @@
                 case MSG_HIDE_KEYBOARD:
                     mIMM.hideSoftInputFromWindow((IBinder) message.obj, 0);
                     return true;
+                case MSG_SET_ORIENTATION:
+                    ((Activity) message.obj).setRequestedOrientation(message.arg1);
+                    return true;
+                case MSG_RUN_COMMAND:
+                    ((AsyncCommand) message.obj).execute(message.arg1, message.arg2);
+                    return true;
             }
             return false;
         }
     }
+
+    public interface AsyncCommand {
+
+        void execute(int arg1, int arg2);
+    }
 }
diff --git a/src/com/android/launcher3/util/ViewCache.java b/src/com/android/launcher3/util/ViewCache.java
new file mode 100644
index 0000000..08b8744
--- /dev/null
+++ b/src/com/android/launcher3/util/ViewCache.java
@@ -0,0 +1,73 @@
+/*
+ * 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 android.content.Context;
+import android.util.SparseArray;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Utility class to cache views at an activity level
+ */
+public class ViewCache {
+
+    protected final SparseArray<CacheEntry> mCache = new SparseArray();
+
+    public void setCacheSize(int layoutId, int size) {
+        mCache.put(layoutId, new CacheEntry(size));
+    }
+
+    public <T extends View> T getView(int layoutId, Context context, ViewGroup parent) {
+        CacheEntry entry = mCache.get(layoutId);
+        if (entry == null) {
+            entry = new CacheEntry(1);
+            mCache.put(layoutId, entry);
+        }
+
+        if (entry.mCurrentSize > 0) {
+            entry.mCurrentSize --;
+            T result = (T) entry.mViews[entry.mCurrentSize];
+            entry.mViews[entry.mCurrentSize] = null;
+            return result;
+        }
+
+        return (T) LayoutInflater.from(context).inflate(layoutId, parent, false);
+    }
+
+    public void recycleView(int layoutId, View view) {
+        CacheEntry entry = mCache.get(layoutId);
+        if (entry != null && entry.mCurrentSize < entry.mMaxSize) {
+            entry.mViews[entry.mCurrentSize] = view;
+            entry.mCurrentSize++;
+        }
+    }
+
+    private static class CacheEntry {
+
+        final int mMaxSize;
+        final View[] mViews;
+
+        int mCurrentSize;
+
+        public CacheEntry(int maxSize) {
+            mMaxSize = maxSize;
+            mViews = new View[maxSize];
+            mCurrentSize = 0;
+        }
+    }
+}
diff --git a/src/com/android/launcher3/util/ViewPool.java b/src/com/android/launcher3/util/ViewPool.java
new file mode 100644
index 0000000..8af048d
--- /dev/null
+++ b/src/com/android/launcher3/util/ViewPool.java
@@ -0,0 +1,115 @@
+/*
+ * 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 android.content.Context;
+import android.os.Handler;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.launcher3.util.ViewPool.Reusable;
+
+import androidx.annotation.AnyThread;
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+
+/**
+ * Utility class to maintain a pool of reusable views.
+ * During initialization, views are inflated on the background thread.
+ */
+public class ViewPool<T extends View & Reusable> {
+
+    private final Object[] mPool;
+
+    private final LayoutInflater mInflater;
+    private final ViewGroup mParent;
+    private final int mLayoutId;
+
+    private int mCurrentSize = 0;
+
+    public ViewPool(Context context, @Nullable ViewGroup parent,
+            int layoutId, int maxSize, int initialSize) {
+        mLayoutId = layoutId;
+        mParent = parent;
+        mInflater = LayoutInflater.from(context);
+        mPool = new Object[maxSize];
+
+        if (initialSize > 0) {
+            initPool(initialSize);
+        }
+    }
+
+    @UiThread
+    private void initPool(int initialSize) {
+        Preconditions.assertUIThread();
+        Handler handler = new Handler();
+
+        // Inflate views on a non looper thread. This allows us to catch errors like calling
+        // "new Handler()" in constructor easily.
+        new Thread(() -> {
+            for (int i = 0; i < initialSize; i++) {
+                T view = inflateNewView();
+                handler.post(() -> addToPool(view));
+            }
+        }).start();
+    }
+
+    @UiThread
+    public void recycle(T view) {
+        Preconditions.assertUIThread();
+        view.onRecycle();
+        addToPool(view);
+    }
+
+    @UiThread
+    private void addToPool(T view) {
+        Preconditions.assertUIThread();
+        if (mCurrentSize >= mPool.length) {
+            // pool is full
+            return;
+        }
+
+        mPool[mCurrentSize] = view;
+        mCurrentSize++;
+    }
+
+    @UiThread
+    public T getView() {
+        Preconditions.assertUIThread();
+        if (mCurrentSize > 0) {
+            mCurrentSize--;
+            return (T) mPool[mCurrentSize];
+        }
+        return inflateNewView();
+    }
+
+    @AnyThread
+    private T inflateNewView() {
+        return (T) mInflater.inflate(mLayoutId, mParent, false);
+    }
+
+    /**
+     * Interface to indicate that a view is reusable
+     */
+    public interface Reusable {
+
+        /**
+         * Called when a view is recycled / added back to the pool
+         */
+        void onRecycle();
+    }
+}
diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java
index c8d1457..f948beb 100644
--- a/src/com/android/launcher3/views/AbstractSlideInView.java
+++ b/src/com/android/launcher3/views/AbstractSlideInView.java
@@ -30,7 +30,6 @@
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.touch.SwipeDetector;
@@ -76,7 +75,7 @@
         mScrollInterpolator = Interpolators.SCROLL_CUBIC;
         mSwipeDetector = new SwipeDetector(context, this, SwipeDetector.VERTICAL);
 
-        mOpenCloseAnimator = LauncherAnimUtils.ofPropertyValuesHolder(this);
+        mOpenCloseAnimator = ObjectAnimator.ofPropertyValuesHolder(this);
         mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
@@ -103,28 +102,33 @@
                 directionsToDetectScroll, false);
         mSwipeDetector.onTouchEvent(ev);
         return mSwipeDetector.isDraggingOrSettling()
-                || !mLauncher.getDragLayer().isEventOverView(mContent, ev);
+                || !getPopupContainer().isEventOverView(mContent, ev);
     }
 
     @Override
     public boolean onControllerTouchEvent(MotionEvent ev) {
         mSwipeDetector.onTouchEvent(ev);
-        if (ev.getAction() == MotionEvent.ACTION_UP && mSwipeDetector.isIdleState()) {
+        if (ev.getAction() == MotionEvent.ACTION_UP && mSwipeDetector.isIdleState()
+                && !isOpeningAnimationRunning()) {
             // If we got ACTION_UP without ever starting swipe, close the panel.
-            if (!mLauncher.getDragLayer().isEventOverView(mContent, ev)) {
+            if (!getPopupContainer().isEventOverView(mContent, ev)) {
                 close(true);
             }
         }
         return true;
     }
 
+    private boolean isOpeningAnimationRunning() {
+        return mIsOpen && mOpenCloseAnimator.isRunning();
+    }
+
     /* SwipeDetector.Listener */
 
     @Override
     public void onDragStart(boolean start) { }
 
     @Override
-    public boolean onDrag(float displacement, float velocity) {
+    public boolean onDrag(float displacement) {
         float range = mContent.getHeight();
         displacement = Utilities.boundToRange(displacement, 0, range);
         setTranslationShift(displacement / range);
@@ -155,7 +159,7 @@
             onCloseComplete();
             return;
         }
-        if (!mIsOpen || mOpenCloseAnimator.isRunning()) {
+        if (!mIsOpen) {
             return;
         }
         mOpenCloseAnimator.setValues(
@@ -178,6 +182,10 @@
 
     protected void onCloseComplete() {
         mIsOpen = false;
-        mLauncher.getDragLayer().removeView(this);
+        getPopupContainer().removeView(this);
+    }
+
+    protected BaseDragLayer getPopupContainer() {
+        return mLauncher.getDragLayer();
     }
 }
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
new file mode 100644
index 0000000..0331a86
--- /dev/null
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.views;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.view.ContextThemeWrapper;
+import android.view.View.AccessibilityDelegate;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.graphics.RotationMode;
+import com.android.launcher3.dot.DotInfo;
+
+/**
+ * An interface to be used along with a context for various activities in Launcher. This allows a
+ * generic class to depend on Context subclass instead of an Activity.
+ */
+public interface ActivityContext {
+
+    default boolean finishAutoCancelActionMode() {
+        return false;
+    }
+
+    default DotInfo getDotInfoForItem(ItemInfo info) {
+        return null;
+    }
+
+    /**
+     * For items with tree hierarchy, notifies the activity to invalidate the parent when a root
+     * is invalidated
+     * @param info info associated with a root node.
+     */
+    default void invalidateParent(ItemInfo info) { }
+
+    default AccessibilityDelegate getAccessibilityDelegate() {
+        return null;
+    }
+
+    /**
+     * The root view to support drag-and-drop and popup support.
+     */
+    BaseDragLayer getDragLayer();
+
+    DeviceProfile getDeviceProfile();
+
+    /**
+     * Device profile to be used by UI elements which are shown directly on top of the wallpaper
+     * and whose presentation is tied to the wallpaper (and physical device) and not the activity
+     * configuration.
+     */
+    default DeviceProfile getWallpaperDeviceProfile() {
+        return getDeviceProfile();
+    }
+
+    default RotationMode getRotationMode() {
+        return RotationMode.NORMAL;
+    }
+
+    static ActivityContext lookupContext(Context context) {
+        if (context instanceof ActivityContext) {
+            return (ActivityContext) context;
+        } else if (context instanceof ContextThemeWrapper) {
+            return lookupContext(((ContextWrapper) context).getBaseContext());
+        } else {
+            throw new IllegalArgumentException("Cannot find ActivityContext in parent tree");
+        }
+    }
+}
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index 8457b2b..15f2724 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -16,59 +16,131 @@
 
 package com.android.launcher3.views;
 
-import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
+import static android.view.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_UP;
 
+import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
+import static com.android.launcher3.Utilities.shouldDisableGestures;
+
+import android.annotation.TargetApi;
 import android.content.Context;
+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;
+import android.view.ViewDebug;
 import android.view.ViewGroup;
+import android.view.WindowInsets;
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.FrameLayout;
 
 import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.BaseActivity;
-import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.InsettableFrameLayout;
 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.TouchController;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 
 /**
  * A viewgroup with utility methods for drag-n-drop and touch interception
  */
-public abstract class BaseDragLayer<T extends BaseDraggingActivity> extends InsettableFrameLayout {
+public abstract class BaseDragLayer<T extends Context & ActivityContext>
+        extends InsettableFrameLayout {
 
-    protected final int[] mTmpXY = new int[2];
+    public static final Property<LayoutParams, Integer> LAYOUT_X =
+            new Property<LayoutParams, Integer>(Integer.TYPE, "x") {
+                @Override
+                public Integer get(LayoutParams lp) {
+                    return lp.x;
+                }
+
+                @Override
+                public void set(LayoutParams lp, Integer x) {
+                    lp.x = x;
+                }
+            };
+
+    public static final Property<LayoutParams, Integer> LAYOUT_Y =
+            new Property<LayoutParams, Integer>(Integer.TYPE, "y") {
+                @Override
+                public Integer get(LayoutParams lp) {
+                    return lp.y;
+                }
+
+                @Override
+                public void set(LayoutParams lp, Integer y) {
+                    lp.y = y;
+                }
+            };
+
+    // Touch is being dispatched through the normal view dispatch system
+    private static final int TOUCH_DISPATCHING_VIEW = 1 << 0;
+    // Touch is being dispatched through the normal view dispatch system, and started at the
+    // system gesture region
+    private static final int TOUCH_DISPATCHING_GESTURE = 1 << 1;
+    // Touch is being dispatched through a proxy from InputMonitor
+    private static final int TOUCH_DISPATCHING_PROXY = 1 << 2;
+
+    protected final float[] mTmpXY = new float[2];
+    protected final float[] mTmpRectPoints = new float[4];
     protected final Rect mHitRect = new Rect();
 
+    @ViewDebug.ExportedProperty(category = "launcher")
+    private final RectF mSystemGestureRegion = new RectF();
+    private int mTouchDispatchState = 0;
+
     protected final T mActivity;
     private final MultiValueAlpha mMultiValueAlpha;
 
+    // All the touch controllers for the view
     protected TouchController[] mControllers;
+    // Touch controller which is currently active for the normal view dispatch
     protected TouchController mActiveController;
+    // Touch controller which is being used for the proxy events
+    protected TouchController mProxyTouchController;
+
     private TouchCompleteListener mTouchCompleteListener;
 
     public BaseDragLayer(Context context, AttributeSet attrs, int alphaChannelCount) {
         super(context, attrs);
-        mActivity = (T) BaseActivity.fromContext(context);
+        mActivity = (T) ActivityContext.lookupContext(context);
         mMultiValueAlpha = new MultiValueAlpha(this, alphaChannelCount);
     }
 
+    /**
+     * Same as {@link #isEventOverView(View, MotionEvent, View)} where evView == this drag layer.
+     */
     public boolean isEventOverView(View view, MotionEvent ev) {
         getDescendantRectRelativeToSelf(view, mHitRect);
         return mHitRect.contains((int) ev.getX(), (int) ev.getY());
     }
 
+    /**
+     * Given a motion event in evView's coordinates, return whether the event is within another
+     * view's bounds.
+     */
+    public boolean isEventOverView(View view, MotionEvent ev, View evView) {
+        int[] xy = new int[] {(int) ev.getX(), (int) ev.getY()};
+        getDescendantCoordRelativeToSelf(evView, xy);
+        getDescendantRectRelativeToSelf(view, mHitRect);
+        return mHitRect.contains(xy[0], xy[1]);
+    }
+
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         int action = ev.getAction();
 
-        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+        if (action == ACTION_UP || action == ACTION_CANCEL) {
             if (mTouchCompleteListener != null) {
                 mTouchCompleteListener.onTouchComplete();
             }
@@ -79,21 +151,31 @@
         return findActiveController(ev);
     }
 
-    protected boolean findActiveController(MotionEvent ev) {
-        mActiveController = null;
+    private TouchController findControllerToHandleTouch(MotionEvent ev) {
+        if (shouldDisableGestures(ev)) return null;
 
         AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
         if (topView != null && topView.onControllerInterceptTouchEvent(ev)) {
-            mActiveController = topView;
-            return true;
+            return topView;
         }
 
         for (TouchController controller : mControllers) {
             if (controller.onControllerInterceptTouchEvent(ev)) {
-                mActiveController = controller;
-                return true;
+                return controller;
             }
         }
+        return null;
+    }
+
+    protected boolean findActiveController(MotionEvent ev) {
+        mActiveController = null;
+        if ((mTouchDispatchState & (TOUCH_DISPATCHING_GESTURE | TOUCH_DISPATCHING_PROXY)) == 0) {
+            // Only look for controllers if we are not dispatching from gesture area and proxy is
+            // not active
+            mActiveController = findControllerToHandleTouch(ev);
+
+            if (mActiveController != null) return true;
+        }
         return false;
     }
 
@@ -139,19 +221,21 @@
         if (child instanceof AbstractFloatingView) {
             // Handles the case where the view is removed without being properly closed.
             // This can happen if something goes wrong during a state change/transition.
-            postDelayed(() -> {
-                AbstractFloatingView floatingView = (AbstractFloatingView) child;
-                if (floatingView.isOpen()) {
-                    floatingView.close(false);
-                }
-            }, SINGLE_FRAME_MS);
+            AbstractFloatingView floatingView = (AbstractFloatingView) child;
+            if (floatingView.isOpen()) {
+                postDelayed(() -> floatingView.close(false), SINGLE_FRAME_MS);
+            }
         }
     }
 
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
+        if (TestProtocol.sDebugTracing) {
+            android.util.Log.d(TestProtocol.NO_DRAG_TAG,
+                    "onTouchEvent " + ev);
+        }
         int action = ev.getAction();
-        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+        if (action == ACTION_UP || action == ACTION_CANCEL) {
             if (mTouchCompleteListener != null) {
                 mTouchCompleteListener.onTouchComplete();
             }
@@ -159,6 +243,10 @@
         }
 
         if (mActiveController != null) {
+            if (TestProtocol.sDebugTracing) {
+                android.util.Log.d(TestProtocol.NO_DRAG_TAG,
+                        "onTouchEvent 1");
+            }
             return mActiveController.onControllerTouchEvent(ev);
         } else {
             // In case no child view handled the touch event, we may not get onIntercept anymore
@@ -166,6 +254,79 @@
         }
     }
 
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent ev) {
+        if (TestProtocol.sDebugTracing) {
+            Log.d(TestProtocol.NO_START_TAG, "BaseDragLayer.dispatchTouchEvent " + ev);
+        }
+        switch (ev.getAction()) {
+            case ACTION_DOWN: {
+                float x = ev.getX();
+                float y = ev.getY();
+                mTouchDispatchState |= TOUCH_DISPATCHING_VIEW;
+
+                if ((y < mSystemGestureRegion.top
+                        || x < mSystemGestureRegion.left
+                        || x > (getWidth() - mSystemGestureRegion.right)
+                        || y > (getHeight() - mSystemGestureRegion.bottom))) {
+                    mTouchDispatchState |= TOUCH_DISPATCHING_GESTURE;
+                } else {
+                    mTouchDispatchState &= ~TOUCH_DISPATCHING_GESTURE;
+                }
+                break;
+            }
+            case ACTION_CANCEL:
+            case ACTION_UP:
+                mTouchDispatchState &= ~TOUCH_DISPATCHING_GESTURE;
+                mTouchDispatchState &= ~TOUCH_DISPATCHING_VIEW;
+                break;
+        }
+        super.dispatchTouchEvent(ev);
+
+        // We want to get all events so that mTouchDispatchSource is maintained properly
+        return true;
+    }
+
+    /**
+     * Called before we are about to receive proxy events.
+     *
+     * @return false if we can't handle proxy at this time
+     */
+    public boolean prepareProxyEventStarting() {
+        mProxyTouchController = null;
+        if ((mTouchDispatchState & TOUCH_DISPATCHING_VIEW) != 0 && mActiveController != null) {
+            // We are already dispatching using view system and have an active controller, we can't
+            // handle another controller.
+
+            // This flag was already cleared in proxy ACTION_UP or ACTION_CANCEL. Added here just
+            // to be safe
+            mTouchDispatchState &= ~TOUCH_DISPATCHING_PROXY;
+            return false;
+        }
+
+        mTouchDispatchState |= TOUCH_DISPATCHING_PROXY;
+        return true;
+    }
+
+    /**
+     * Proxies the touch events to the gesture handlers
+     */
+    public boolean proxyTouchEvent(MotionEvent ev) {
+        boolean handled;
+        if (mProxyTouchController != null) {
+            handled = mProxyTouchController.onControllerTouchEvent(ev);
+        } else {
+            mProxyTouchController = findControllerToHandleTouch(ev);
+            handled = mProxyTouchController != null;
+        }
+        int action = ev.getAction();
+        if (action == ACTION_UP || action == ACTION_CANCEL) {
+            mProxyTouchController = null;
+            mTouchDispatchState &= ~TOUCH_DISPATCHING_PROXY;
+        }
+        return handled;
+    }
+
     /**
      * Determine the rect of the descendant in this DragLayer's coordinates
      *
@@ -174,14 +335,16 @@
      * @return The factor by which this descendant is scaled relative to this DragLayer.
      */
     public float getDescendantRectRelativeToSelf(View descendant, Rect r) {
-        mTmpXY[0] = 0;
-        mTmpXY[1] = 0;
-        float scale = getDescendantCoordRelativeToSelf(descendant, mTmpXY);
-
-        r.set(mTmpXY[0], mTmpXY[1],
-                (int) (mTmpXY[0] + scale * descendant.getMeasuredWidth()),
-                (int) (mTmpXY[1] + scale * descendant.getMeasuredHeight()));
-        return scale;
+        mTmpRectPoints[0] = 0;
+        mTmpRectPoints[1] = 0;
+        mTmpRectPoints[2] = descendant.getWidth();
+        mTmpRectPoints[3] = descendant.getHeight();
+        float s = getDescendantCoordRelativeToSelf(descendant, mTmpRectPoints);
+        r.left = Math.round(Math.min(mTmpRectPoints[0], mTmpRectPoints[2]));
+        r.top = Math.round(Math.min(mTmpRectPoints[1], mTmpRectPoints[3]));
+        r.right = Math.round(Math.max(mTmpRectPoints[0], mTmpRectPoints[2]));
+        r.bottom = Math.round(Math.max(mTmpRectPoints[1], mTmpRectPoints[3]));
+        return s;
     }
 
     public float getLocationInDragLayer(View child, int[] loc) {
@@ -191,6 +354,14 @@
     }
 
     public float getDescendantCoordRelativeToSelf(View descendant, int[] coord) {
+        mTmpXY[0] = coord[0];
+        mTmpXY[1] = coord[1];
+        float scale = getDescendantCoordRelativeToSelf(descendant, mTmpXY);
+        Utilities.roundArray(mTmpXY, coord);
+        return scale;
+    }
+
+    public float getDescendantCoordRelativeToSelf(View descendant, float[] coord) {
         return getDescendantCoordRelativeToSelf(descendant, coord, false);
     }
 
@@ -206,17 +377,27 @@
      *         this scale factor is assumed to be equal in X and Y, and so if at any point this
      *         assumption fails, we will need to return a pair of scale factors.
      */
-    public float getDescendantCoordRelativeToSelf(View descendant, int[] coord,
+    public float getDescendantCoordRelativeToSelf(View descendant, float[] coord,
             boolean includeRootScroll) {
         return Utilities.getDescendantCoordRelativeToAncestor(descendant, this,
                 coord, includeRootScroll);
     }
 
     /**
+     * Inverse of {@link #getDescendantCoordRelativeToSelf(View, float[])}.
+     */
+    public void mapCoordInSelfToDescendant(View descendant, float[] coord) {
+        Utilities.mapCoordInSelfToDescendant(descendant, this, coord);
+    }
+
+    /**
      * Inverse of {@link #getDescendantCoordRelativeToSelf(View, int[])}.
      */
     public void mapCoordInSelfToDescendant(View descendant, int[] coord) {
-        Utilities.mapCoordInSelfToDescendant(descendant, this, coord);
+        mTmpXY[0] = coord[0];
+        mTmpXY[1] = coord[1];
+        Utilities.mapCoordInSelfToDescendant(descendant, this, mTmpXY);
+        Utilities.roundArray(mTmpXY, coord);
     }
 
     public void getViewRectRelativeToSelf(View v, Rect r) {
@@ -293,6 +474,15 @@
         return mMultiValueAlpha.getProperty(index);
     }
 
+    public void dump(String prefix, PrintWriter writer) {
+        writer.println(prefix + "DragLayer");
+        if (mActiveController != null) {
+            writer.println(prefix + "\tactiveController: " + mActiveController);
+            mActiveController.dump(prefix + "\t", writer);
+        }
+        writer.println(prefix + "\tdragLayerAlpha : " + mMultiValueAlpha );
+    }
+
     public static class LayoutParams extends InsettableFrameLayout.LayoutParams {
         public int x, y;
         public boolean customPosition = false;
@@ -308,38 +498,6 @@
         public LayoutParams(ViewGroup.LayoutParams lp) {
             super(lp);
         }
-
-        public void setWidth(int width) {
-            this.width = width;
-        }
-
-        public int getWidth() {
-            return width;
-        }
-
-        public void setHeight(int height) {
-            this.height = height;
-        }
-
-        public int getHeight() {
-            return height;
-        }
-
-        public void setX(int x) {
-            this.x = x;
-        }
-
-        public int getX() {
-            return x;
-        }
-
-        public void setY(int y) {
-            this.y = y;
-        }
-
-        public int getY() {
-            return y;
-        }
     }
 
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
@@ -356,4 +514,15 @@
             }
         }
     }
+
+    @Override
+    @TargetApi(Build.VERSION_CODES.Q)
+    public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
+        if (Utilities.ATLEAST_Q) {
+            Insets gestureInsets = insets.getMandatorySystemGestureInsets();
+            mSystemGestureRegion.set(gestureInsets.left, gestureInsets.top,
+                    gestureInsets.right, gestureInsets.bottom);
+        }
+        return super.dispatchApplyWindowInsets(insets);
+    }
 }
diff --git a/src/com/android/launcher3/views/BottomUserEducationView.java b/src/com/android/launcher3/views/BottomUserEducationView.java
index a291fc6..bdc69af 100644
--- a/src/com/android/launcher3/views/BottomUserEducationView.java
+++ b/src/com/android/launcher3/views/BottomUserEducationView.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.views;
 
+import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
+
 import android.animation.PropertyValuesHolder;
 import android.content.Context;
 import android.graphics.Rect;
@@ -28,8 +30,7 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.Interpolators;
-
-import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 
 public class BottomUserEducationView extends AbstractSlideInView implements Insettable {
 
@@ -71,6 +72,11 @@
     }
 
     @Override
+    public int getLogContainerType() {
+        return ContainerType.TIP;
+    }
+
+    @Override
     protected boolean isOfType(int type) {
         return (type & TYPE_ON_BOARD_POPUP) != 0;
     }
diff --git a/src/com/android/launcher3/views/ButtonPreference.java b/src/com/android/launcher3/views/ButtonPreference.java
deleted file mode 100644
index fdcf2ca..0000000
--- a/src/com/android/launcher3/views/ButtonPreference.java
+++ /dev/null
@@ -1,65 +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.views;
-
-import android.content.Context;
-import android.preference.Preference;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * Extension of {@link Preference} which makes the widget layout clickable.
- *
- * @see #setWidgetLayoutResource(int)
- */
-public class ButtonPreference extends Preference {
-
-    private boolean mWidgetFrameVisible = false;
-
-    public ButtonPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-    }
-
-    public ButtonPreference(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    public ButtonPreference(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public ButtonPreference(Context context) {
-        super(context);
-    }
-
-    public void setWidgetFrameVisible(boolean isVisible) {
-        if (mWidgetFrameVisible != isVisible) {
-            mWidgetFrameVisible = isVisible;
-            notifyChanged();
-        }
-    }
-
-    @Override
-    protected void onBindView(View view) {
-        super.onBindView(view);
-
-        ViewGroup widgetFrame = view.findViewById(android.R.id.widget_frame);
-        if (widgetFrame != null) {
-            widgetFrame.setVisibility(mWidgetFrameVisible ? View.VISIBLE : View.GONE);
-        }
-    }
-}
diff --git a/src/com/android/launcher3/views/ClipPathView.java b/src/com/android/launcher3/views/ClipPathView.java
new file mode 100644
index 0000000..2152e1d
--- /dev/null
+++ b/src/com/android/launcher3/views/ClipPathView.java
@@ -0,0 +1,27 @@
+/*
+ * 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.views;
+
+import android.graphics.Path;
+import android.view.View;
+
+/**
+ * Alternative to using {@link View#getClipToOutline()} as it only works with derivatives of
+ * rounded rect.
+ */
+public interface ClipPathView {
+    void setClipPath(Path clipPath);
+}
diff --git a/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java b/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java
index a11a8c5..d89e7f8 100644
--- a/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java
+++ b/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java
@@ -16,11 +16,12 @@
 
 package com.android.launcher3.views;
 
+import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
+
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.Color;
-import android.support.v4.graphics.ColorUtils;
 import android.util.AttributeSet;
 import android.widget.TextView;
 
@@ -59,20 +60,20 @@
 
         // We enhance the shadow by drawing the shadow twice
         getPaint().setShadowLayer(mShadowInfo.ambientShadowBlur, 0, 0,
-                ColorUtils.setAlphaComponent(mShadowInfo.ambientShadowColor, alpha));
+                setColorAlphaBound(mShadowInfo.ambientShadowColor, alpha));
 
-        drawWithoutBadge(canvas);
+        drawWithoutDot(canvas);
         canvas.save();
         canvas.clipRect(getScrollX(), getScrollY() + getExtendedPaddingTop(),
                 getScrollX() + getWidth(),
                 getScrollY() + getHeight());
 
         getPaint().setShadowLayer(mShadowInfo.keyShadowBlur, 0.0f, mShadowInfo.keyShadowOffset,
-                ColorUtils.setAlphaComponent(mShadowInfo.keyShadowColor, alpha));
-        drawWithoutBadge(canvas);
+                setColorAlphaBound(mShadowInfo.keyShadowColor, alpha));
+        drawWithoutDot(canvas);
         canvas.restore();
 
-        drawBadgeIfNecessary(canvas);
+        drawDotIfNecessary(canvas);
     }
 
     public static class ShadowInfo {
@@ -106,11 +107,11 @@
                 return true;
             } else if (ambientShadowAlpha > 0) {
                 textView.getPaint().setShadowLayer(ambientShadowBlur, 0, 0,
-                        ColorUtils.setAlphaComponent(ambientShadowColor, textAlpha));
+                        setColorAlphaBound(ambientShadowColor, textAlpha));
                 return true;
             } else if (keyShadowAlpha > 0) {
                 textView.getPaint().setShadowLayer(keyShadowBlur, 0.0f, keyShadowOffset,
-                        ColorUtils.setAlphaComponent(keyShadowColor, textAlpha));
+                        setColorAlphaBound(keyShadowColor, textAlpha));
                 return true;
             } else {
                 return false;
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
new file mode 100644
index 0000000..ab4b576
--- /dev/null
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -0,0 +1,887 @@
+/*
+ * 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.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.Utilities.mapToRange;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
+import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
+
+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.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Outline;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.widget.ImageView;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.InsettableFrameLayout.LayoutParams;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
+import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.graphics.IconShape;
+import com.android.launcher3.graphics.ShiftedBitmapDrawable;
+import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.shortcuts.DeepShortcutView;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+import androidx.annotation.WorkerThread;
+import androidx.dynamicanimation.animation.FloatPropertyCompat;
+import androidx.dynamicanimation.animation.SpringAnimation;
+import androidx.dynamicanimation.animation.SpringForce;
+
+/**
+ * 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 View implements
+        Animator.AnimatorListener, ClipPathView, OnGlobalLayoutListener {
+
+    private static final String TAG = FloatingIconView.class.getSimpleName();
+
+    // Manages loading the icon on a worker thread
+    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 Rect sTmpRect = new Rect();
+    private static final RectF sTmpRectF = new RectF();
+    private static final Object[] sTmpObjArray = new Object[1];
+
+    // We spring the foreground drawable relative to the icon's movement in the DragLayer.
+    // We then use these two factor values to scale the movement of the fg within this view.
+    private static final int FG_TRANS_X_FACTOR = 60;
+    private static final int FG_TRANS_Y_FACTOR = 75;
+
+    private static final FloatPropertyCompat<FloatingIconView> mFgTransYProperty
+            = new FloatPropertyCompat<FloatingIconView>("FloatingViewFgTransY") {
+        @Override
+        public float getValue(FloatingIconView view) {
+            return view.mFgTransY;
+        }
+
+        @Override
+        public void setValue(FloatingIconView view, float transY) {
+            view.mFgTransY = transY;
+            view.invalidate();
+        }
+    };
+
+    private static final FloatPropertyCompat<FloatingIconView> mFgTransXProperty
+            = new FloatPropertyCompat<FloatingIconView>("FloatingViewFgTransX") {
+        @Override
+        public float getValue(FloatingIconView view) {
+            return view.mFgTransX;
+        }
+
+        @Override
+        public void setValue(FloatingIconView view, float transX) {
+            view.mFgTransX = transX;
+            view.invalidate();
+        }
+    };
+
+    private Runnable mEndRunnable;
+    private CancellationSignal mLoadIconSignal;
+
+    private final Launcher mLauncher;
+    private final int mBlurSizeOutline;
+
+    private boolean mIsVerticalBarLayout = false;
+    private boolean mIsAdaptiveIcon = false;
+    private boolean mIsOpening;
+
+    private IconLoadResult mIconLoadResult;
+
+    private @Nullable Drawable mBadge;
+    private @Nullable Drawable mForeground;
+    private @Nullable Drawable mBackground;
+    private float mRotation;
+    private ValueAnimator mRevealAnimator;
+    private final Rect mStartRevealRect = new Rect();
+    private final Rect mEndRevealRect = new Rect();
+    private Path mClipPath;
+    private float mTaskCornerRadius;
+
+    private View mOriginalIcon;
+    private RectF mPositionOut;
+    private Runnable mOnTargetChangeRunnable;
+
+    private final Rect mOutline = new Rect();
+    private final Rect mFinalDrawableBounds = new Rect();
+
+    private AnimatorSet mFadeAnimatorSet;
+    private ListenerView mListenerView;
+
+    private final SpringAnimation mFgSpringY;
+    private float mFgTransY;
+    private final SpringAnimation mFgSpringX;
+    private float mFgTransX;
+
+    public FloatingIconView(Context context) {
+        this(context, null);
+    }
+
+    public FloatingIconView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public FloatingIconView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        mLauncher = Launcher.getLauncher(context);
+        mBlurSizeOutline = getResources().getDimensionPixelSize(
+                R.dimen.blur_size_medium_outline);
+        mListenerView = new ListenerView(context, attrs);
+
+        mFgSpringX = new SpringAnimation(this, mFgTransXProperty)
+                .setSpring(new SpringForce()
+                .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
+                .setStiffness(SpringForce.STIFFNESS_LOW));
+        mFgSpringY = new SpringAnimation(this, mFgTransYProperty)
+                .setSpring(new SpringForce()
+                        .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
+                        .setStiffness(SpringForce.STIFFNESS_LOW));
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        if (!mIsOpening) {
+            getViewTreeObserver().addOnGlobalLayoutListener(this);
+            mLauncher.getRotationHelper().setCurrentTransitionRequest(REQUEST_LOCK);
+        }
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        getViewTreeObserver().removeOnGlobalLayoutListener(this);
+        super.onDetachedFromWindow();
+    }
+
+    /**
+     * Positions this view to match the size and location of {@param rect}.
+     * @param alpha The alpha to set this view.
+     * @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}.
+     */
+    public void update(RectF rect, float alpha, float progress, float shapeProgressStart,
+            float cornerRadius, boolean isOpening) {
+        setAlpha(alpha);
+
+        LayoutParams lp = (LayoutParams) getLayoutParams();
+        float dX = rect.left - lp.leftMargin;
+        float dY = rect.top - lp.topMargin;
+        setTranslationX(dX);
+        setTranslationY(dY);
+
+        float minSize = Math.min(lp.width, lp.height);
+        float scaleX = rect.width() / minSize;
+        float scaleY = rect.height() / minSize;
+        float scale = Math.max(1f, Math.min(scaleX, scaleY));
+
+        setPivotX(0);
+        setPivotY(0);
+        setScaleX(scale);
+        setScaleY(scale);
+
+        // 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);
+
+        if (mIsVerticalBarLayout) {
+            mOutline.right = (int) (rect.width() / scale);
+        } else {
+            mOutline.bottom = (int) (rect.height() / scale);
+        }
+
+        mTaskCornerRadius = cornerRadius / scale;
+        if (mIsAdaptiveIcon) {
+            if (!isOpening && progress >= shapeProgressStart) {
+                if (mRevealAnimator == null) {
+                    mRevealAnimator = (ValueAnimator) IconShape.getShape().createRevealAnimator(
+                            this, mStartRevealRect, mOutline, mTaskCornerRadius, !isOpening);
+                    mRevealAnimator.addListener(new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            mRevealAnimator = null;
+                        }
+                    });
+                    mRevealAnimator.start();
+                    // We pause here so we can set the current fraction ourselves.
+                    mRevealAnimator.pause();
+                }
+                mRevealAnimator.setCurrentFraction(shapeRevealProgress);
+            }
+
+            float drawableScale = (mIsVerticalBarLayout ? mOutline.width() : mOutline.height())
+                    / minSize;
+            setBackgroundDrawableBounds(drawableScale);
+            if (isOpening) {
+                // Center align foreground
+                int height = mFinalDrawableBounds.height();
+                int width = mFinalDrawableBounds.width();
+                int diffY = mIsVerticalBarLayout ? 0
+                        : (int) (((height * drawableScale) - height) / 2);
+                int diffX = mIsVerticalBarLayout ? (int) (((width * drawableScale) - width) / 2)
+                        : 0;
+                sTmpRect.set(mFinalDrawableBounds);
+                sTmpRect.offset(diffX, diffY);
+                mForeground.setBounds(sTmpRect);
+            } else {
+                // Spring the foreground relative to the icon's movement within the DragLayer.
+                int diffX = (int) (dX / mLauncher.getDeviceProfile().availableWidthPx
+                        * FG_TRANS_X_FACTOR);
+                int diffY = (int) (dY / mLauncher.getDeviceProfile().availableHeightPx
+                        * FG_TRANS_Y_FACTOR);
+
+                mFgSpringX.animateToFinalPosition(diffX);
+                mFgSpringY.animateToFinalPosition(diffY);
+            }
+        }
+        invalidate();
+        invalidateOutline();
+    }
+
+    @Override
+    public void onAnimationEnd(Animator animator) {
+        if (mLoadIconSignal != null) {
+            mLoadIconSignal.cancel();
+        }
+        if (mEndRunnable != null) {
+            mEndRunnable.run();
+        } else {
+            // End runnable also ends the reveal animator, so we manually handle it here.
+            if (mRevealAnimator != null) {
+                mRevealAnimator.end();
+            }
+        }
+    }
+
+    /**
+     * Sets the size and position of this view to match {@param v}.
+     *
+     * @param v The view to copy
+     * @param positionOut Rect that will hold the size and position of v.
+     */
+    private void matchPositionOf(Launcher launcher, View v, boolean isOpening, RectF positionOut) {
+        float rotation = getLocationBoundsForView(launcher, v, isOpening, positionOut);
+        final LayoutParams lp = new LayoutParams(
+                Math.round(positionOut.width()),
+                Math.round(positionOut.height()));
+        updatePosition(rotation, positionOut, lp);
+        setLayoutParams(lp);
+    }
+
+    private void updatePosition(float rotation, RectF position, LayoutParams lp) {
+        mRotation = rotation;
+        mPositionOut.set(position);
+        lp.ignoreInsets = true;
+        // Position the floating view exactly on top of the original
+        lp.leftMargin = Math.round(position.left);
+        lp.topMargin = Math.round(position.top);
+
+        // Set the properties here already to make sure they are available when running the first
+        // animation frame.
+        layout(lp.leftMargin, lp.topMargin, lp.leftMargin + lp.width, lp.topMargin
+                + lp.height);
+
+    }
+
+    /**
+     * Gets the location bounds of a view and returns the overall rotation.
+     * - For DeepShortcutView, we return the bounds of the icon view.
+     * - For BubbleTextView, we return the icon bounds.
+     */
+    private static float getLocationBoundsForView(Launcher launcher, View v, boolean isOpening,
+            RectF outRect) {
+        boolean ignoreTransform = !isOpening;
+        if (v instanceof DeepShortcutView) {
+            v = ((DeepShortcutView) v).getBubbleText();
+            ignoreTransform = false;
+        } else if (v.getParent() instanceof DeepShortcutView) {
+            v = ((DeepShortcutView) v.getParent()).getIconView();
+            ignoreTransform = false;
+        }
+        if (v == null) {
+            return 0;
+        }
+
+        Rect iconBounds = new Rect();
+        if (v instanceof BubbleTextView) {
+            ((BubbleTextView) v).getIconBounds(iconBounds);
+        } else if (v instanceof FolderIcon) {
+            ((FolderIcon) v).getPreviewBounds(iconBounds);
+        } else {
+            iconBounds.set(0, 0, v.getWidth(), v.getHeight());
+        }
+
+        float[] points = new float[] {iconBounds.left, iconBounds.top, iconBounds.right,
+                iconBounds.bottom};
+        float[] rotation = new float[] {0};
+        Utilities.getDescendantCoordRelativeToAncestor(v, launcher.getDragLayer(), points,
+                false, ignoreTransform, rotation);
+        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]));
+        return rotation[0];
+    }
+
+    /**
+     * Loads the icon and saves the results to {@link #sIconLoadResult}.
+     * Runs onIconLoaded callback (if any), which signifies that the FloatingIconView is
+     * ready to display the icon. Otherwise, the FloatingIconView will grab the results when its
+     * initialized.
+     *
+     * @param originalView The View that the FloatingIconView will replace.
+     * @param info ItemInfo of the originalView
+     * @param pos The position of the view.
+     */
+    @WorkerThread
+    @SuppressWarnings("WrongThread")
+    private static void getIconResult(Launcher l, View originalView, ItemInfo info, RectF pos,
+            IconLoadResult iconLoadResult) {
+        Drawable drawable = null;
+        Drawable badge = null;
+        boolean supportsAdaptiveIcons = ADAPTIVE_ICON_WINDOW_ANIM.get()
+                && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
+        Drawable btvIcon = originalView instanceof BubbleTextView
+                ? ((BubbleTextView) originalView).getIcon() : null;
+        if (info instanceof SystemShortcut) {
+            if (originalView instanceof ImageView) {
+                drawable = ((ImageView) originalView).getDrawable();
+            } else if (originalView instanceof DeepShortcutView) {
+                drawable = ((DeepShortcutView) originalView).getIconView().getBackground();
+            } else {
+                drawable = originalView.getBackground();
+            }
+        } else {
+            boolean isFolderIcon = originalView instanceof FolderIcon;
+            int width = isFolderIcon ? originalView.getWidth() : (int) pos.width();
+            int height = isFolderIcon ? originalView.getHeight() : (int) pos.height();
+            if (supportsAdaptiveIcons) {
+                drawable = getFullDrawable(l, info, width, height, false, sTmpObjArray);
+                if (drawable instanceof AdaptiveIconDrawable) {
+                    badge = getBadge(l, info, sTmpObjArray[0]);
+                } else {
+                    // The drawable we get back is not an adaptive icon, so we need to use the
+                    // BubbleTextView icon that is already legacy treated.
+                    drawable = btvIcon;
+                }
+            } else {
+                if (originalView instanceof BubbleTextView) {
+                    // Similar to DragView, we simply use the BubbleTextView icon here.
+                    drawable = btvIcon;
+                } else {
+                    drawable = getFullDrawable(l, info, width, height, false, sTmpObjArray);
+                }
+            }
+        }
+
+        drawable = drawable == null ? null : drawable.getConstantState().newDrawable();
+        int iconOffset = getOffsetForIconBounds(l, drawable, pos);
+        synchronized (iconLoadResult) {
+            iconLoadResult.drawable = drawable;
+            iconLoadResult.badge = badge;
+            iconLoadResult.iconOffset = iconOffset;
+            if (iconLoadResult.onIconLoaded != null) {
+                l.getMainExecutor().execute(iconLoadResult.onIconLoaded);
+                iconLoadResult.onIconLoaded = null;
+            }
+            iconLoadResult.isIconLoaded = true;
+        }
+    }
+
+    /**
+     * Sets the drawables of the {@param originalView} onto this view.
+     *
+     * @param originalView The View that the FloatingIconView will replace.
+     * @param drawable The drawable of the original view.
+     * @param badge The badge of the original view.
+     * @param iconOffset The amount of offset needed to match this view with the original view.
+     */
+    @UiThread
+    private void setIcon(View originalView, @Nullable Drawable drawable, @Nullable Drawable badge,
+            int iconOffset) {
+        mBadge = badge;
+
+        mIsAdaptiveIcon = drawable instanceof AdaptiveIconDrawable;
+        if (mIsAdaptiveIcon) {
+            boolean isFolderIcon = drawable instanceof FolderAdaptiveIcon;
+
+            AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) drawable;
+            Drawable background = adaptiveIcon.getBackground();
+            if (background == null) {
+                background = new ColorDrawable(Color.TRANSPARENT);
+            }
+            mBackground = background;
+            Drawable foreground = adaptiveIcon.getForeground();
+            if (foreground == null) {
+                foreground = new ColorDrawable(Color.TRANSPARENT);
+            }
+            mForeground = foreground;
+
+            final LayoutParams lp = (LayoutParams) getLayoutParams();
+            final int originalHeight = lp.height;
+            final int originalWidth = lp.width;
+
+            int blurMargin = mBlurSizeOutline / 2;
+            mFinalDrawableBounds.set(0, 0, originalWidth, originalHeight);
+
+            if (!isFolderIcon) {
+                mFinalDrawableBounds.inset(iconOffset - blurMargin, iconOffset - blurMargin);
+            }
+            mForeground.setBounds(mFinalDrawableBounds);
+            mBackground.setBounds(mFinalDrawableBounds);
+
+            mStartRevealRect.set(0, 0, originalWidth, originalHeight);
+
+            if (mBadge != null) {
+                mBadge.setBounds(mStartRevealRect);
+                if (!mIsOpening && !isFolderIcon) {
+                    DRAWABLE_ALPHA.set(mBadge, 0);
+                }
+            }
+
+            if (isFolderIcon) {
+                ((FolderIcon) originalView).getPreviewBounds(sTmpRect);
+                float bgStroke = ((FolderIcon) originalView).getBackgroundStrokeWidth();
+                if (mForeground instanceof ShiftedBitmapDrawable) {
+                    ShiftedBitmapDrawable sbd = (ShiftedBitmapDrawable) mForeground;
+                    sbd.setShiftX(sbd.getShiftX() - sTmpRect.left - bgStroke);
+                    sbd.setShiftY(sbd.getShiftY() - sTmpRect.top - bgStroke);
+                }
+                if (mBadge instanceof ShiftedBitmapDrawable) {
+                    ShiftedBitmapDrawable sbd = (ShiftedBitmapDrawable) mBadge;
+                    sbd.setShiftX(sbd.getShiftX() - sTmpRect.left - bgStroke);
+                    sbd.setShiftY(sbd.getShiftY() - sTmpRect.top - bgStroke);
+                }
+            } else {
+                Utilities.scaleRectAboutCenter(mStartRevealRect,
+                        IconShape.getNormalizationScale());
+            }
+
+            float aspectRatio = mLauncher.getDeviceProfile().aspectRatio;
+            if (mIsVerticalBarLayout) {
+                lp.width = (int) Math.max(lp.width, lp.height * aspectRatio);
+            } else {
+                lp.height = (int) Math.max(lp.height, lp.width * aspectRatio);
+            }
+            layout(lp.leftMargin, lp.topMargin, lp.leftMargin + lp.width, lp.topMargin
+                    + lp.height);
+
+            float scale = Math.max((float) lp.height / originalHeight,
+                    (float) lp.width / originalWidth);
+            float bgDrawableStartScale;
+            if (mIsOpening) {
+                bgDrawableStartScale = 1f;
+                mOutline.set(0, 0, originalWidth, originalHeight);
+            } else {
+                bgDrawableStartScale = scale;
+                mOutline.set(0, 0, lp.width, lp.height);
+            }
+            setBackgroundDrawableBounds(bgDrawableStartScale);
+            mEndRevealRect.set(0, 0, lp.width, lp.height);
+            setOutlineProvider(new ViewOutlineProvider() {
+                @Override
+                public void getOutline(View view, Outline outline) {
+                    outline.setRoundRect(mOutline, mTaskCornerRadius);
+                }
+            });
+            setClipToOutline(true);
+        } else {
+            setBackground(drawable);
+            setClipToOutline(false);
+        }
+
+        invalidate();
+        invalidateOutline();
+    }
+
+    /**
+     * 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.
+     */
+    private void checkIconResult(View originalView, boolean isOpening) {
+        CancellationSignal cancellationSignal = new CancellationSignal();
+        if (!isOpening) {
+            // Hide immediately since the floating view starts at a different location.
+            originalView.setVisibility(INVISIBLE);
+            cancellationSignal.setOnCancelListener(() -> originalView.setVisibility(VISIBLE));
+        }
+
+        if (mIconLoadResult == null) {
+            Log.w(TAG, "No icon load result found in checkIconResult");
+            return;
+        }
+
+        synchronized (mIconLoadResult) {
+            if (mIconLoadResult.isIconLoaded) {
+                setIcon(originalView, mIconLoadResult.drawable, mIconLoadResult.badge,
+                        mIconLoadResult.iconOffset);
+                if (isOpening) {
+                    originalView.setVisibility(INVISIBLE);
+                }
+            } else {
+                mIconLoadResult.onIconLoaded = () -> {
+                    if (cancellationSignal.isCanceled()) {
+                        return;
+                    }
+
+                    setIcon(originalView, mIconLoadResult.drawable, mIconLoadResult.badge,
+                            mIconLoadResult.iconOffset);
+
+                    // Delay swapping views until the icon is loaded to prevent a flash.
+                    setVisibility(VISIBLE);
+                    originalView.setVisibility(INVISIBLE);
+                };
+                mLoadIconSignal = cancellationSignal;
+            }
+        }
+    }
+
+    private void setBackgroundDrawableBounds(float scale) {
+        sTmpRect.set(mFinalDrawableBounds);
+        Utilities.scaleRectAboutCenter(sTmpRect, scale);
+        // Since the drawable is at the top of the view, we need to offset to keep it centered.
+        if (mIsVerticalBarLayout) {
+            sTmpRect.offsetTo((int) (mFinalDrawableBounds.left * scale), sTmpRect.top);
+        } else {
+            sTmpRect.offsetTo(sTmpRect.left, (int) (mFinalDrawableBounds.top * scale));
+        }
+        mBackground.setBounds(sTmpRect);
+    }
+
+    @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)) {
+            return 0;
+        }
+        int blurSizeOutline =
+                l.getResources().getDimensionPixelSize(R.dimen.blur_size_medium_outline);
+
+        Rect bounds = new Rect(0, 0, (int) position.width() + blurSizeOutline,
+                (int) position.height() + blurSizeOutline);
+        bounds.inset(blurSizeOutline / 2, blurSizeOutline / 2);
+
+        try (LauncherIcons li = LauncherIcons.obtain(l)) {
+            Utilities.scaleRectAboutCenter(bounds, li.getNormalizer().getScale(drawable, null,
+                    null, null));
+        }
+
+        bounds.inset(
+                (int) (-bounds.width() * AdaptiveIconDrawable.getExtraInsetFraction()),
+                (int) (-bounds.height() * AdaptiveIconDrawable.getExtraInsetFraction())
+        );
+
+        return bounds.left;
+    }
+
+    @Override
+    public void setClipPath(Path clipPath) {
+        mClipPath = clipPath;
+        invalidate();
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        int count = canvas.save();
+        canvas.rotate(mRotation,
+                mFinalDrawableBounds.exactCenterX(), mFinalDrawableBounds.exactCenterY());
+        if (mClipPath != null) {
+            canvas.clipPath(mClipPath);
+        }
+        super.draw(canvas);
+        if (mBackground != null) {
+            mBackground.draw(canvas);
+        }
+        if (mForeground != null) {
+            int count2 = canvas.save();
+            canvas.translate(mFgTransX, mFgTransY);
+            mForeground.draw(canvas);
+            canvas.restoreToCount(count2);
+        }
+        if (mBadge != null) {
+            mBadge.draw(canvas);
+        }
+        canvas.restoreToCount(count);
+    }
+
+    public void onListenerViewClosed() {
+        // Fast finish here.
+        if (mEndRunnable != null) {
+            mEndRunnable.run();
+            mEndRunnable = null;
+        }
+        if (mFadeAnimatorSet != null) {
+            mFadeAnimatorSet.end();
+            mFadeAnimatorSet = null;
+        }
+    }
+
+    @Override
+    public void onAnimationStart(Animator animator) {
+        if (mIconLoadResult != null && mIconLoadResult.isIconLoaded) {
+            setVisibility(View.VISIBLE);
+        }
+    }
+
+    @Override
+    public void onAnimationCancel(Animator animator) {}
+
+    @Override
+    public void onAnimationRepeat(Animator animator) {}
+
+    @Override
+    public void onGlobalLayout() {
+        if (mOriginalIcon.isAttachedToWindow() && mPositionOut != null) {
+            float rotation = getLocationBoundsForView(mLauncher, mOriginalIcon, mIsOpening,
+                    sTmpRectF);
+            if (rotation != mRotation || !sTmpRectF.equals(mPositionOut)) {
+                updatePosition(rotation, sTmpRectF, (LayoutParams) getLayoutParams());
+                if (mOnTargetChangeRunnable != null) {
+                    mOnTargetChangeRunnable.run();
+                }
+            }
+        }
+    }
+
+    public void setOnTargetChangeListener(Runnable onTargetChangeListener) {
+        mOnTargetChangeRunnable = onTargetChangeListener;
+    }
+
+    /**
+     * Loads the icon drawable on a worker thread to reduce latency between swapping views.
+     */
+    @UiThread
+    public static IconLoadResult fetchIcon(Launcher l, View v, ItemInfo info, boolean isOpening) {
+        IconLoadResult result = new IconLoadResult();
+        new Handler(LauncherModel.getWorkerLooper()).postAtFrontOfQueue(() -> {
+            RectF position = new RectF();
+            getLocationBoundsForView(l, v, isOpening, position);
+            getIconResult(l, v, info, position, result);
+        });
+
+        sIconLoadResult = result;
+        return result;
+    }
+
+    /**
+     * Creates a floating icon view for {@param originalView}.
+     * @param originalView The view to copy
+     * @param hideOriginal If true, it will hide {@param originalView} while this view is visible.
+     *                     Else, we will not draw anything in this view.
+     * @param positionOut Rect that will hold the size and position of v.
+     * @param isOpening True if this view replaces the icon for app open animation.
+     */
+    public static FloatingIconView getFloatingIconView(Launcher launcher, View originalView,
+            boolean hideOriginal, RectF positionOut, boolean isOpening) {
+        final DragLayer dragLayer = launcher.getDragLayer();
+        ViewGroup parent = (ViewGroup) dragLayer.getParent();
+        FloatingIconView view = launcher.getViewCache().getView(R.layout.floating_icon_view,
+                launcher, parent);
+        view.recycle();
+
+        // Get the drawable on the background thread
+        boolean shouldLoadIcon = originalView.getTag() instanceof ItemInfo && hideOriginal;
+        view.mIconLoadResult = sIconLoadResult;
+        if (shouldLoadIcon && view.mIconLoadResult == null) {
+            view.mIconLoadResult = fetchIcon(launcher, originalView,
+                    (ItemInfo) originalView.getTag(), isOpening);
+        }
+        sIconLoadResult = null;
+
+        view.mIsVerticalBarLayout = launcher.getDeviceProfile().isVerticalBarLayout();
+        view.mIsOpening = isOpening;
+        view.mOriginalIcon = originalView;
+        view.mPositionOut = positionOut;
+
+        // Match the position of the original view.
+        view.matchPositionOf(launcher, originalView, isOpening, positionOut);
+
+        // Must be called after matchPositionOf so that we know what size to load.
+        if (shouldLoadIcon) {
+            view.checkIconResult(originalView, isOpening);
+        }
+
+        // We need to add it to the overlay, but keep it invisible until animation starts..
+        view.setVisibility(INVISIBLE);
+        parent.addView(view);
+        dragLayer.addView(view.mListenerView);
+        view.mListenerView.setListener(view::onListenerViewClosed);
+
+        view.mEndRunnable = () -> {
+            view.mEndRunnable = null;
+
+            if (hideOriginal) {
+                if (isOpening) {
+                    originalView.setVisibility(VISIBLE);
+                    view.finish(dragLayer);
+                } else {
+                    view.mFadeAnimatorSet = view.createFadeAnimation(originalView, dragLayer);
+                    view.mFadeAnimatorSet.start();
+                }
+            } else {
+                view.finish(dragLayer);
+            }
+        };
+        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 (mBadge != null && !(mOriginalIcon instanceof FolderIcon)) {
+            ObjectAnimator badgeFade = ObjectAnimator.ofInt(mBadge, DRAWABLE_ALPHA, 255);
+            badgeFade.addUpdateListener(valueAnimator -> invalidate());
+            fade.play(badgeFade);
+        }
+
+        if (originalView instanceof BubbleTextView) {
+            BubbleTextView btv = (BubbleTextView) originalView;
+            btv.forceHideDot(true);
+            fade.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    btv.forceHideDot(false);
+                }
+            });
+        }
+
+        if (originalView instanceof FolderIcon) {
+            FolderIcon folderIcon = (FolderIcon) originalView;
+            folderIcon.setBackgroundVisible(false);
+            folderIcon.getFolderName().setTextVisibility(false);
+            fade.play(folderIcon.getFolderName().createTextAlphaAnimator(true));
+            fade.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    folderIcon.setBackgroundVisible(true);
+                    if (folderIcon.hasDot()) {
+                        folderIcon.animateDotScale(0, 1f);
+                    }
+                }
+            });
+        } else {
+            fade.play(ObjectAnimator.ofFloat(originalView, ALPHA, 0f, 1f));
+        }
+
+        return fade;
+    }
+
+    private void finish(DragLayer dragLayer) {
+        ((ViewGroup) dragLayer.getParent()).removeView(this);
+        dragLayer.removeView(mListenerView);
+        recycle();
+        mLauncher.getViewCache().recycleView(R.layout.floating_icon_view, this);
+    }
+
+    private void recycle() {
+        setTranslationX(0);
+        setTranslationY(0);
+        setScaleX(1);
+        setScaleY(1);
+        setAlpha(1);
+        setBackground(null);
+        if (mLoadIconSignal != null) {
+            mLoadIconSignal.cancel();
+        }
+        mLoadIconSignal = null;
+        mEndRunnable = null;
+        mIsAdaptiveIcon = false;
+        mForeground = null;
+        mBackground = null;
+        mClipPath = null;
+        mFinalDrawableBounds.setEmpty();
+        if (mRevealAnimator != null) {
+            mRevealAnimator.cancel();
+        }
+        mRevealAnimator = null;
+        if (mFadeAnimatorSet != null) {
+            mFadeAnimatorSet.cancel();
+        }
+        mPositionOut = null;
+        mFadeAnimatorSet = null;
+        mListenerView.setListener(null);
+        mOriginalIcon = null;
+        mOnTargetChangeRunnable = null;
+        mTaskCornerRadius = 0;
+        mOutline.setEmpty();
+        mFgTransY = 0;
+        mFgSpringX.cancel();
+        mFgTransX = 0;
+        mFgSpringY.cancel();
+        mBadge = null;
+        sTmpObjArray[0] = null;
+        mIconLoadResult = null;
+    }
+
+    private static class IconLoadResult {
+        Drawable drawable;
+        Drawable badge;
+        int iconOffset;
+        Runnable onIconLoaded;
+        boolean isIconLoaded;
+    }
+}
diff --git a/src/com/android/launcher3/views/ListenerView.java b/src/com/android/launcher3/views/ListenerView.java
new file mode 100644
index 0000000..263f7c4
--- /dev/null
+++ b/src/com/android/launcher3/views/ListenerView.java
@@ -0,0 +1,86 @@
+/*
+ * 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.views;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.launcher3.AbstractFloatingView;
+
+/**
+ * An invisible AbstractFloatingView that can run a callback when it is being closed.
+ */
+public class ListenerView extends AbstractFloatingView {
+
+    public Runnable mCloseListener;
+
+    public ListenerView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setVisibility(View.GONE);
+    }
+
+    public void setListener(Runnable listener) {
+        mCloseListener = listener;
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mIsOpen = true;
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        mIsOpen = false;
+    }
+
+    @Override
+    protected void handleClose(boolean animate) {
+        if (mIsOpen) {
+            if (mCloseListener != null) {
+                mCloseListener.run();
+            } else {
+                if (getParent() instanceof ViewGroup) {
+                    ((ViewGroup) getParent()).removeView(this);
+                }
+            }
+        }
+        mIsOpen = false;
+    }
+
+    @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 (ev.getAction() == MotionEvent.ACTION_DOWN) {
+            handleClose(false);
+        }
+        // We want other views to be able to intercept the touch so we return false here.
+        return false;
+    }
+}
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index db4c492..63f7427 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -15,11 +15,13 @@
  */
 package com.android.launcher3.views;
 
-import static com.android.launcher3.BaseDraggingActivity.INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION;
+import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_FLAVOR;
 import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_OFFSET;
 
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ResolveInfo;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.text.TextUtils;
@@ -34,6 +36,7 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.popup.ArrowPopup;
 import com.android.launcher3.shortcuts.DeepShortcutView;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
@@ -43,6 +46,8 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import androidx.annotation.VisibleForTesting;
+
 /**
  * Popup shown on long pressing an empty space in launcher
  */
@@ -94,7 +99,7 @@
         if (ev.getAction() != MotionEvent.ACTION_DOWN) {
             return false;
         }
-        if (mLauncher.getDragLayer().isEventOverView(this, ev)) {
+        if (getPopupContainer().isEventOverView(this, ev)) {
             return false;
         }
         close(true);
@@ -133,6 +138,11 @@
         popup.reorderAndShow(popup.getChildCount());
     }
 
+    @VisibleForTesting
+    public static ArrowPopup getOptionsPopup(Launcher launcher) {
+        return launcher.findViewById(R.id.deep_shortcuts_container);
+    }
+
     public static void showDefaultOptions(Launcher launcher, float x, float y) {
         float halfSize = launcher.getResources().getDimension(R.dimen.options_menu_thumb_size) / 2;
         if (x < 0 || y < 0) {
@@ -142,10 +152,16 @@
         RectF target = new RectF(x - halfSize, y - halfSize, x + halfSize, y + halfSize);
 
         ArrayList<OptionItem> options = new ArrayList<>();
-        options.add(new OptionItem(R.string.wallpaper_button_text, R.drawable.ic_wallpaper,
+        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,
                 ControlType.WALLPAPER_BUTTON, OptionsPopupView::startWallpaperPicker));
-        options.add(new OptionItem(R.string.widget_button_text, R.drawable.ic_widget,
-                ControlType.WIDGETS_BUTTON, OptionsPopupView::onWidgetsClicked));
+        if (!FeatureFlags.GO_DISABLE_WIDGETS) {
+            options.add(new OptionItem(R.string.widget_button_text, R.drawable.ic_widget,
+                    ControlType.WIDGETS_BUTTON, OptionsPopupView::onWidgetsClicked));
+        }
         options.add(new OptionItem(R.string.settings_button_text, R.drawable.ic_setting,
                 ControlType.SETTINGS_BUTTON, OptionsPopupView::startSettings));
 
@@ -185,18 +201,19 @@
             return false;
         }
         Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER)
+                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
                 .putExtra(EXTRA_WALLPAPER_OFFSET,
                         launcher.getWorkspace().getWallpaperOffsetForCenterPage());
-        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
-
+        if (!Utilities.existsStyleWallpapers(launcher)) {
+            intent.putExtra(EXTRA_WALLPAPER_FLAVOR, "wallpaper_only");
+        } else {
+            intent.putExtra(EXTRA_WALLPAPER_FLAVOR, "focus_wallpaper");
+        }
         String pickerPackage = launcher.getString(R.string.wallpaper_picker_package);
         if (!TextUtils.isEmpty(pickerPackage)) {
             intent.setPackage(pickerPackage);
-        } else {
-            // If there is no target package, use the default intent chooser animation
-            intent.putExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION, true);
         }
-        return launcher.startActivitySafely(v, intent, null);
+        return launcher.startActivitySafely(v, intent, null, null);
     }
 
     public static class OptionItem {
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index 05bab8b..5653801 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -24,7 +24,7 @@
 import android.graphics.Paint;
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.support.v7.widget.RecyclerView;
+import android.graphics.RectF;
 import android.util.AttributeSet;
 import android.util.Property;
 import android.view.MotionEvent;
@@ -32,13 +32,17 @@
 import android.view.ViewConfiguration;
 import android.widget.TextView;
 
+import androidx.recyclerview.widget.RecyclerView;
+
 import com.android.launcher3.BaseRecyclerView;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.graphics.FastScrollThumbDrawable;
 import com.android.launcher3.util.Themes;
 
+import java.util.Collections;
+import java.util.List;
+
 /**
  * The track and scrollbar that shows when you scroll the list.
  */
@@ -65,6 +69,9 @@
     private final static int SCROLL_BAR_VIS_DURATION = 150;
     private static final float FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR = 0.75f;
 
+    private static final List<Rect> SYSTEM_GESTURE_EXCLUSION_RECT =
+            Collections.singletonList(new Rect());
+
     private final int mMinWidth;
     private final int mMaxWidth;
     private final int mThumbPadding;
@@ -81,6 +88,8 @@
 
     private final Paint mThumbPaint;
     protected final int mThumbHeight;
+    private final RectF mThumbBounds = new RectF();
+    private final Point mThumbDrawOffset = new Point();
 
     private final Paint mTrackPaint;
 
@@ -175,6 +184,7 @@
         if (mThumbOffsetY == y) {
             return;
         }
+        updatePopupY((int) y);
         mThumbOffsetY = y;
         invalidate();
     }
@@ -224,8 +234,7 @@
                 }
                 if (isNearThumb(x, y)) {
                     mTouchOffsetY = mDownY - mThumbOffsetY;
-                } else if (FeatureFlags.LAUNCHER3_DIRECT_SCROLL
-                        && mRv.supportsFastScrolling()
+                } else if (mRv.supportsFastScrolling()
                         && isNearScrollBar(mDownX)) {
                     calcTouchOffsetAndPrepToFastScroll(mDownY, mLastY);
                     updateFastScrollSectionNameAndThumbOffset(mLastY, y);
@@ -282,7 +291,6 @@
             mPopupView.setText(sectionName);
         }
         animatePopupVisibility(!sectionName.isEmpty());
-        updatePopupY(lastY);
         mLastTouchY = boundedY;
         setThumbOffsetY((int) mLastTouchY);
     }
@@ -293,18 +301,29 @@
         }
         int saveCount = canvas.save();
         canvas.translate(getWidth() / 2, mRv.getScrollBarTop());
+        mThumbDrawOffset.set(getWidth() / 2, mRv.getScrollBarTop());
         // Draw the track
         float halfW = mWidth / 2;
         canvas.drawRoundRect(-halfW, 0, halfW, mRv.getScrollbarTrackHeight(),
                 mWidth, mWidth, mTrackPaint);
 
         canvas.translate(0, mThumbOffsetY);
+        mThumbDrawOffset.y += mThumbOffsetY;
         halfW += mThumbPadding;
-        float r = mWidth + mThumbPadding + mThumbPadding;
-        canvas.drawRoundRect(-halfW, 0, halfW, mThumbHeight, r, r, mThumbPaint);
+        float r = getScrollThumbRadius();
+        mThumbBounds.set(-halfW, 0, halfW, mThumbHeight);
+        canvas.drawRoundRect(mThumbBounds, r, r, mThumbPaint);
+        if (Utilities.ATLEAST_Q) {
+            mThumbBounds.roundOut(SYSTEM_GESTURE_EXCLUSION_RECT.get(0));
+            SYSTEM_GESTURE_EXCLUSION_RECT.get(0).offset(mThumbDrawOffset.x, mThumbDrawOffset.y);
+            setSystemGestureExclusionRects(SYSTEM_GESTURE_EXCLUSION_RECT);
+        }
         canvas.restoreToCount(saveCount);
     }
 
+    private float getScrollThumbRadius() {
+        return mWidth + mThumbPadding + mThumbPadding;
+    }
 
     /**
      * Animates the width of the scrollbar.
@@ -353,11 +372,15 @@
     }
 
     private void updatePopupY(int lastTouchY) {
+        if (!mPopupVisible) {
+            return;
+        }
         int height = mPopupView.getHeight();
-        float top = lastTouchY - (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * height)
-                + mRv.getScrollBarTop();
-        top = Utilities.boundToRange(top,
-                mMaxWidth, mRv.getScrollbarTrackHeight() - mMaxWidth - height);
+        // Aligns the rounded corner of the pop up with the top of the thumb.
+        float top = mRv.getScrollBarTop() + lastTouchY + (getScrollThumbRadius() / 2f)
+                - (height / 2f);
+        top = Utilities.boundToRange(top, 0,
+                getTop() + mRv.getScrollBarTop() + mRv.getScrollbarTrackHeight() - height);
         mPopupView.setTranslationY(top);
     }
 
diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java
index 7066980..c360117 100644
--- a/src/com/android/launcher3/views/ScrimView.java
+++ b/src/com/android/launcher3/views/ScrimView.java
@@ -16,14 +16,15 @@
 package com.android.launcher3.views;
 
 import static android.content.Context.ACCESSIBILITY_SERVICE;
-import static android.support.v4.graphics.ColorUtils.compositeColors;
-import static android.support.v4.graphics.ColorUtils.setAlphaComponent;
 import static android.view.MotionEvent.ACTION_DOWN;
 
 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.anim.Interpolators.DEACCEL;
+import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
+
+import static androidx.core.graphics.ColorUtils.compositeColors;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -38,12 +39,6 @@
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
-import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
-import android.support.v4.widget.ExploreByTouchHelper;
 import android.util.AttributeSet;
 import android.util.Property;
 import android.view.KeyEvent;
@@ -64,10 +59,19 @@
 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 java.util.List;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.view.ViewCompat;
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
+import androidx.customview.widget.ExploreByTouchHelper;
+
 /**
  * Simple scrim which draws a flat color
  */
@@ -90,6 +94,7 @@
     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 final Rect mTempRect = new Rect();
     private final int[] mTempPos = new int[2];
@@ -113,6 +118,8 @@
     private final Rect mDragHandleBounds;
     private final RectF mHitRect = new RectF();
 
+    private final MultiValueAlpha mMultiValueAlpha;
+
     private final AccessibilityHelper mAccessibilityHelper;
     @Nullable
     protected Drawable mDragHandle;
@@ -136,6 +143,11 @@
 
         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
@@ -180,7 +192,7 @@
     @Override
     public void onExtractedColorsChanged(WallpaperColorInfo wallpaperColorInfo) {
         mScrimColor = wallpaperColorInfo.getMainColor();
-        mEndFlatColor = compositeColors(mEndScrim, setAlphaComponent(
+        mEndFlatColor = compositeColors(mEndScrim, setColorAlphaBound(
                 mScrimColor, Math.round(mMaxScrimAlpha * 255)));
         mEndFlatColorAlpha = Color.alpha(mEndFlatColor);
         updateColors();
@@ -199,7 +211,7 @@
     public void reInitUi() { }
 
     protected void updateColors() {
-        mCurrentFlatColor = mProgress >= 1 ? 0 : setAlphaComponent(
+        mCurrentFlatColor = mProgress >= 1 ? 0 : setColorAlphaBound(
                 mEndFlatColor, Math.round((1 - mProgress) * mEndFlatColorAlpha));
     }
 
@@ -313,7 +325,7 @@
 
         if (enabled) {
             stateManager.addStateListener(this);
-            onStateSetImmediately(mLauncher.getStateManager().getState());
+            handleStateChangedComplete(mLauncher.getStateManager().getState());
         } else {
             setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
         }
@@ -359,12 +371,11 @@
 
     @Override
     public void onStateTransitionComplete(LauncherState finalState) {
-        onStateSetImmediately(finalState);
+        handleStateChangedComplete(finalState);
     }
 
-    @Override
-    public void onStateSetImmediately(LauncherState state) {
-        setImportantForAccessibility(state == ALL_APPS
+    private void handleStateChangedComplete(LauncherState finalState) {
+        setImportantForAccessibility(finalState == ALL_APPS
                 ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
                 : IMPORTANT_FOR_ACCESSIBILITY_AUTO);
     }
diff --git a/src/com/android/launcher3/views/Snackbar.java b/src/com/android/launcher3/views/Snackbar.java
new file mode 100644
index 0000000..dc0e2e0
--- /dev/null
+++ b/src/com/android/launcher3/views/Snackbar.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.views;
+
+import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS;
+import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_TEXT;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.widget.TextView;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.compat.AccessibilityManagerCompat;
+import com.android.launcher3.dragndrop.DragLayer;
+
+/**
+ * A toast-like UI at the bottom of the screen with a label, button action, and dismiss action.
+ */
+public class Snackbar extends AbstractFloatingView {
+
+    private static final long SHOW_DURATION_MS = 180;
+    private static final long HIDE_DURATION_MS = 180;
+    private static final int TIMEOUT_DURATION_MS = 4000;
+
+    private final Launcher mLauncher;
+    private Runnable mOnDismissed;
+
+    public Snackbar(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public Snackbar(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        mLauncher = Launcher.getLauncher(context);
+        inflate(context, R.layout.snackbar, this);
+    }
+
+    public static void show(Launcher launcher, int labelStringResId, int actionStringResId,
+            Runnable onDismissed, Runnable onActionClicked) {
+        closeOpenViews(launcher, true, TYPE_SNACKBAR);
+        Snackbar snackbar = new Snackbar(launcher, null);
+        // Set some properties here since inflated xml only contains the children.
+        snackbar.setOrientation(HORIZONTAL);
+        snackbar.setGravity(Gravity.CENTER_VERTICAL);
+        Resources res = launcher.getResources();
+        snackbar.setElevation(res.getDimension(R.dimen.snackbar_elevation));
+        int padding = res.getDimensionPixelSize(R.dimen.snackbar_padding);
+        snackbar.setPadding(padding, padding, padding, padding);
+        snackbar.setBackgroundResource(R.drawable.round_rect_primary);
+
+        snackbar.mIsOpen = true;
+        DragLayer dragLayer = launcher.getDragLayer();
+        dragLayer.addView(snackbar);
+
+        DragLayer.LayoutParams params = (DragLayer.LayoutParams) snackbar.getLayoutParams();
+        params.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
+        params.height = res.getDimensionPixelSize(R.dimen.snackbar_height);
+        int maxMarginLeftRight = res.getDimensionPixelSize(R.dimen.snackbar_max_margin_left_right);
+        int minMarginLeftRight = res.getDimensionPixelSize(R.dimen.snackbar_min_margin_left_right);
+        int marginBottom = res.getDimensionPixelSize(R.dimen.snackbar_margin_bottom);
+        Rect insets = launcher.getDeviceProfile().getInsets();
+        int maxWidth = dragLayer.getWidth() - minMarginLeftRight * 2 - insets.left - insets.right;
+        int minWidth = dragLayer.getWidth() - maxMarginLeftRight * 2 - insets.left - insets.right;
+        params.width = minWidth;
+        params.setMargins(0, 0, 0, marginBottom + insets.bottom);
+
+        TextView labelView = snackbar.findViewById(R.id.label);
+        TextView actionView = snackbar.findViewById(R.id.action);
+        String labelText = res.getString(labelStringResId);
+        String actionText = res.getString(actionStringResId);
+        int totalContentWidth = (int) (labelView.getPaint().measureText(labelText)
+                + actionView.getPaint().measureText(actionText))
+                + labelView.getPaddingRight() + labelView.getPaddingLeft()
+                + actionView.getPaddingRight() + actionView.getPaddingLeft()
+                + padding * 2;
+        if (totalContentWidth > params.width) {
+            // The text doesn't fit in our standard width so update width to accommodate.
+            if (totalContentWidth <= maxWidth) {
+                params.width = totalContentWidth;
+            } else {
+                // One line will be cut off, fallback to 2 lines and smaller font. (This should only
+                // happen in some languages if system display and font size are set to largest.)
+                int textHeight = res.getDimensionPixelSize(R.dimen.snackbar_content_height);
+                float textSizePx = res.getDimension(R.dimen.snackbar_min_text_size);
+                labelView.setLines(2);
+                labelView.getLayoutParams().height = textHeight * 2;
+                actionView.getLayoutParams().height = textHeight * 2;
+                labelView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSizePx);
+                actionView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSizePx);
+                params.height += textHeight;
+                params.width = maxWidth;
+            }
+        }
+        labelView.setText(labelText);
+        actionView.setText(actionText);
+        actionView.setOnClickListener(v -> {
+            if (onActionClicked != null) {
+                onActionClicked.run();
+            }
+            snackbar.mOnDismissed = null;
+            snackbar.close(true);
+        });
+        snackbar.mOnDismissed = onDismissed;
+
+        snackbar.setAlpha(0);
+        snackbar.setScaleX(0.8f);
+        snackbar.setScaleY(0.8f);
+        snackbar.animate()
+                .alpha(1f)
+                .withLayer()
+                .scaleX(1)
+                .scaleY(1)
+                .setDuration(SHOW_DURATION_MS)
+                .setInterpolator(Interpolators.ACCEL_DEACCEL)
+                .start();
+        int timeout = AccessibilityManagerCompat.getRecommendedTimeoutMillis(launcher,
+                TIMEOUT_DURATION_MS, FLAG_CONTENT_TEXT | FLAG_CONTENT_CONTROLS);
+        snackbar.postDelayed(() -> snackbar.close(true), timeout);
+    }
+
+    @Override
+    protected void handleClose(boolean animate) {
+        if (mIsOpen) {
+            if (animate) {
+                animate().alpha(0f)
+                        .withLayer()
+                        .setStartDelay(0)
+                        .setDuration(HIDE_DURATION_MS)
+                        .setInterpolator(Interpolators.ACCEL)
+                        .withEndAction(this::onClosed)
+                        .start();
+            } else {
+                animate().cancel();
+                onClosed();
+            }
+            mIsOpen = false;
+        }
+    }
+
+    private void onClosed() {
+        mLauncher.getDragLayer().removeView(this);
+        if (mOnDismissed != null) {
+            mOnDismissed.run();
+        }
+    }
+
+    @Override
+    public void logActionCommand(int command) {
+        // TODO
+    }
+
+    @Override
+    protected boolean isOfType(int type) {
+        return (type & TYPE_SNACKBAR) != 0;
+    }
+
+    @Override
+    public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+            DragLayer dl = mLauncher.getDragLayer();
+            if (!dl.isEventOverView(this, ev)) {
+                close(true);
+            }
+        }
+        return false;
+    }
+}
diff --git a/src/com/android/launcher3/views/SpringRelativeLayout.java b/src/com/android/launcher3/views/SpringRelativeLayout.java
index b0313ce..d0ec9d7 100644
--- a/src/com/android/launcher3/views/SpringRelativeLayout.java
+++ b/src/com/android/launcher3/views/SpringRelativeLayout.java
@@ -15,24 +15,25 @@
  */
 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.support.animation.DynamicAnimation;
-import android.support.animation.FloatPropertyCompat;
-import android.support.animation.SpringAnimation;
-import android.support.animation.SpringForce;
-import android.support.annotation.NonNull;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.RecyclerView.EdgeEffectFactory;
 import android.util.AttributeSet;
 import android.util.SparseBooleanArray;
 import android.view.View;
 import android.widget.EdgeEffect;
 import android.widget.RelativeLayout;
 
-import static android.support.animation.SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY;
-import static android.support.animation.SpringForce.STIFFNESS_LOW;
-import static android.support.animation.SpringForce.STIFFNESS_MEDIUM;
+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;
 
 public class SpringRelativeLayout extends RelativeLayout {
 
diff --git a/src/com/android/launcher3/views/Transposable.java b/src/com/android/launcher3/views/Transposable.java
new file mode 100644
index 0000000..929c1aa
--- /dev/null
+++ b/src/com/android/launcher3/views/Transposable.java
@@ -0,0 +1,26 @@
+/**
+ * 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.views;
+
+import com.android.launcher3.graphics.RotationMode;
+
+/**
+ * Indicates that a view can be transposed.
+ */
+public interface Transposable {
+
+    RotationMode getRotationMode();
+}
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index 10708d6..72cddc7 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -15,6 +15,9 @@
  */
 package com.android.launcher3.widget;
 
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+
+import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
 import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
 
 import android.content.Context;
@@ -31,29 +34,44 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.dragndrop.DragOptions;
-import com.android.launcher3.graphics.ColorScrim;
+import com.android.launcher3.popup.PopupDataProvider;
 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 com.android.launcher3.views.BaseDragLayer;
 
 /**
  * Base class for various widgets popup
  */
 abstract class BaseWidgetSheet extends AbstractSlideInView
-        implements OnClickListener, OnLongClickListener, DragSource {
+        implements OnClickListener, OnLongClickListener, DragSource,
+        PopupDataProvider.PopupDataChangeListener {
 
 
     /* Touch handling related member variables. */
     private Toast mWidgetInstructionToast;
 
-    protected final ColorScrim mColorScrim;
+    protected final View mColorScrim;
 
     public BaseWidgetSheet(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        mColorScrim = ColorScrim.createExtractedColorScrim(this);
+        mColorScrim = createColorScrim(context);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mLauncher.getPopupDataProvider().setChangeListener(this);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        mLauncher.getPopupDataProvider().setChangeListener(null);
     }
 
     @Override
@@ -71,7 +89,7 @@
     }
 
     @Override
-    public final boolean onLongClick(View v) {
+    public boolean onLongClick(View v) {
         if (!ItemLongClickListener.canStartDrag(mLauncher)) return false;
 
         if (v instanceof WidgetCell) {
@@ -80,9 +98,14 @@
         return true;
     }
 
+    protected void attachToContainer() {
+        getPopupContainer().addView(mColorScrim);
+        getPopupContainer().addView(this);
+    }
+
     protected void setTranslationShift(float translationShift) {
         super.setTranslationShift(translationShift);
-        mColorScrim.setProgress(1 - mTranslationShift);
+        mColorScrim.setAlpha(1 - mTranslationShift);
     }
 
     private boolean beginDraggingWidget(WidgetCell v) {
@@ -96,7 +119,7 @@
         }
 
         int[] loc = new int[2];
-        mLauncher.getDragLayer().getLocationInDragLayer(image, loc);
+        getPopupContainer().getLocationInDragLayer(image, loc);
 
         new PendingItemDragHelper(v).startDrag(
                 image.getBitmapBounds(), image.getBitmap().getWidth(), image.getWidth(),
@@ -115,17 +138,18 @@
 
     protected void onCloseComplete() {
         super.onCloseComplete();
+        getPopupContainer().removeView(mColorScrim);
         clearNavBarColor();
     }
 
     protected void clearNavBarColor() {
-        mLauncher.getSystemUiController().updateUiState(
+        getSystemUiController().updateUiState(
                 SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET, 0);
     }
 
     protected void setupNavBarColor() {
-        boolean isSheetDark = Themes.getAttrBoolean(mLauncher, R.attr.isMainColorDark);
-        mLauncher.getSystemUiController().updateUiState(
+        boolean isSheetDark = Themes.getAttrBoolean(getContext(), R.attr.isMainColorDark);
+        getSystemUiController().updateUiState(
                 SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET,
                 isSheetDark ? SystemUiController.FLAG_DARK_NAV : SystemUiController.FLAG_LIGHT_NAV);
     }
@@ -138,11 +162,34 @@
 
     @Override
     public final void logActionCommand(int command) {
-        Target target = newContainerTarget(ContainerType.WIDGETS);
+        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();
+    }
+
+    private static View createColorScrim(Context context) {
+        View view = new View(context);
+        view.forceHasOverlappingRendering(false);
+
+        WallpaperColorInfo colors = WallpaperColorInfo.getInstance(context);
+        int alpha = context.getResources().getInteger(R.integer.extracted_color_gradient_alpha);
+        view.setBackgroundColor(setColorAlphaBound(colors.getSecondaryColor(), alpha));
+
+        BaseDragLayer.LayoutParams lp = new BaseDragLayer.LayoutParams(MATCH_PARENT, MATCH_PARENT);
+        lp.ignoreInsets = true;
+        view.setLayoutParams(lp);
+
+        return view;
+    }
 }
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index 12859c7..dce839f 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -16,16 +16,13 @@
 
 package com.android.launcher3.widget;
 
-import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.PointF;
-import android.graphics.Rect;
 import android.os.Handler;
 import android.os.SystemClock;
 import android.util.SparseBooleanArray;
-import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
@@ -47,14 +44,13 @@
 import com.android.launcher3.StylusEventHelper;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.BaseDragLayer.TouchCompleteListener;
 
-import java.util.ArrayList;
-
 /**
  * {@inheritDoc}
  */
-public class LauncherAppWidgetHostView extends AppWidgetHostView
+public class LauncherAppWidgetHostView extends NavigableAppWidgetHostView
         implements TouchCompleteListener, View.OnLongClickListener {
 
     // Related to the auto-advancing of widgets
@@ -75,9 +71,6 @@
 
     private float mSlop;
 
-    @ViewDebug.ExportedProperty(category = "launcher")
-    private boolean mChildrenFocused;
-
     private boolean mIsScrollable;
     private boolean mIsAttachedToWindow;
     private boolean mIsAutoAdvanceRegistered;
@@ -105,6 +98,9 @@
         if (Utilities.ATLEAST_OREO) {
             setExecutor(Utilities.THREAD_POOL_EXECUTOR);
         }
+        if (Utilities.ATLEAST_Q && Themes.getAttrBoolean(mLauncher, R.attr.isWorkspaceDarkText)) {
+            setOnLightBackground(true);
+        }
     }
 
     @Override
@@ -220,7 +216,8 @@
                 }
                 break;
         }
-        return false;
+        // We want to keep receiving though events to be able to cancel long press on ACTION_UP
+        return true;
     }
 
     @Override
@@ -267,98 +264,6 @@
         }
     }
 
-    @Override
-    public int getDescendantFocusability() {
-        return mChildrenFocused ? ViewGroup.FOCUS_BEFORE_DESCENDANTS
-                : ViewGroup.FOCUS_BLOCK_DESCENDANTS;
-    }
-
-    @Override
-    public boolean dispatchKeyEvent(KeyEvent event) {
-        if (mChildrenFocused && event.getKeyCode() == KeyEvent.KEYCODE_ESCAPE
-                && event.getAction() == KeyEvent.ACTION_UP) {
-            mChildrenFocused = false;
-            requestFocus();
-            return true;
-        }
-        return super.dispatchKeyEvent(event);
-    }
-
-    @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event) {
-        if (!mChildrenFocused && keyCode == KeyEvent.KEYCODE_ENTER) {
-            event.startTracking();
-            return true;
-        }
-        return super.onKeyDown(keyCode, event);
-    }
-
-    @Override
-    public boolean onKeyUp(int keyCode, KeyEvent event) {
-        if (event.isTracking()) {
-            if (!mChildrenFocused && keyCode == KeyEvent.KEYCODE_ENTER) {
-                mChildrenFocused = true;
-                ArrayList<View> focusableChildren = getFocusables(FOCUS_FORWARD);
-                focusableChildren.remove(this);
-                int childrenCount = focusableChildren.size();
-                switch (childrenCount) {
-                    case 0:
-                        mChildrenFocused = false;
-                        break;
-                    case 1: {
-                        if (getTag() instanceof ItemInfo) {
-                            ItemInfo item = (ItemInfo) getTag();
-                            if (item.spanX == 1 && item.spanY == 1) {
-                                focusableChildren.get(0).performClick();
-                                mChildrenFocused = false;
-                                return true;
-                            }
-                        }
-                        // continue;
-                    }
-                    default:
-                        focusableChildren.get(0).requestFocus();
-                        return true;
-                }
-            }
-        }
-        return super.onKeyUp(keyCode, event);
-    }
-
-    @Override
-    protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
-        if (gainFocus) {
-            mChildrenFocused = false;
-            dispatchChildFocus(false);
-        }
-        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
-    }
-
-    @Override
-    public void requestChildFocus(View child, View focused) {
-        super.requestChildFocus(child, focused);
-        dispatchChildFocus(mChildrenFocused && focused != null);
-        if (focused != null) {
-            focused.setFocusableInTouchMode(false);
-        }
-    }
-
-    @Override
-    public void clearChildFocus(View child) {
-        super.clearChildFocus(child);
-        dispatchChildFocus(false);
-    }
-
-    @Override
-    public boolean dispatchUnhandledMove(View focused, int direction) {
-        return mChildrenFocused;
-    }
-
-    private void dispatchChildFocus(boolean childIsFocused) {
-        // The host view's background changes when selected, to indicate the focus is inside.
-        setSelected(childIsFocused);
-    }
-
     public void switchToErrorView() {
         // Update the widget with 0 Layout id, to reset the view to error view.
         updateAppWidget(new RemoteViews(getAppWidgetInfo().provider.getPackageName(), 0));
@@ -502,4 +407,13 @@
         mLauncher.removeItem(this, info, false  /* deleteFromDb */);
         mLauncher.bindAppWidget(info);
     }
+
+    @Override
+    protected boolean shouldAllowDirectClick() {
+        if (getTag() instanceof ItemInfo) {
+            ItemInfo item = (ItemInfo) getTag();
+            return item.spanX == 1 && item.spanY == 1;
+        }
+        return false;
+    }
 }
diff --git a/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java b/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java
new file mode 100644
index 0000000..68b1595
--- /dev/null
+++ b/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java
@@ -0,0 +1,136 @@
+/*
+ * 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.widget;
+
+import android.appwidget.AppWidgetHostView;
+import android.content.Context;
+import android.graphics.Rect;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.ViewDebug;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+
+/**
+ * Extension of AppWidgetHostView with support for controlled keyboard navigation.
+ */
+public abstract class NavigableAppWidgetHostView extends AppWidgetHostView {
+
+    @ViewDebug.ExportedProperty(category = "launcher")
+    private boolean mChildrenFocused;
+
+    public NavigableAppWidgetHostView(Context context) {
+        super(context);
+    }
+
+    @Override
+    public int getDescendantFocusability() {
+        return mChildrenFocused ? ViewGroup.FOCUS_BEFORE_DESCENDANTS
+                : ViewGroup.FOCUS_BLOCK_DESCENDANTS;
+    }
+
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        if (mChildrenFocused && event.getKeyCode() == KeyEvent.KEYCODE_ESCAPE
+                && event.getAction() == KeyEvent.ACTION_UP) {
+            mChildrenFocused = false;
+            requestFocus();
+            return true;
+        }
+        return super.dispatchKeyEvent(event);
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        if (!mChildrenFocused && keyCode == KeyEvent.KEYCODE_ENTER) {
+            event.startTracking();
+            return true;
+        }
+        return super.onKeyDown(keyCode, event);
+    }
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        if (event.isTracking()) {
+            if (!mChildrenFocused && keyCode == KeyEvent.KEYCODE_ENTER) {
+                mChildrenFocused = true;
+                ArrayList<View> focusableChildren = getFocusables(FOCUS_FORWARD);
+                focusableChildren.remove(this);
+                int childrenCount = focusableChildren.size();
+                switch (childrenCount) {
+                    case 0:
+                        mChildrenFocused = false;
+                        break;
+                    case 1: {
+                        if (shouldAllowDirectClick()) {
+                            focusableChildren.get(0).performClick();
+                            mChildrenFocused = false;
+                            return true;
+                        }
+                        // continue;
+                    }
+                    default:
+                        focusableChildren.get(0).requestFocus();
+                        return true;
+                }
+            }
+        }
+        return super.onKeyUp(keyCode, event);
+    }
+
+    /**
+     * For a widget with only a single interactive element, return true if whole widget should act
+     * as a single interactive element, and clicking 'enter' should activate the child element
+     * directly. Otherwise clicking 'enter' will only move the focus inside the widget.
+     */
+    protected abstract boolean shouldAllowDirectClick();
+
+    @Override
+    protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
+        if (gainFocus) {
+            mChildrenFocused = false;
+            dispatchChildFocus(false);
+        }
+        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+    }
+
+    @Override
+    public void requestChildFocus(View child, View focused) {
+        super.requestChildFocus(child, focused);
+        dispatchChildFocus(mChildrenFocused && focused != null);
+        if (focused != null) {
+            focused.setFocusableInTouchMode(false);
+        }
+    }
+
+    @Override
+    public void clearChildFocus(View child) {
+        super.clearChildFocus(child);
+        dispatchChildFocus(false);
+    }
+
+    @Override
+    public boolean dispatchUnhandledMove(View focused, int direction) {
+        return mChildrenFocused;
+    }
+
+    private void dispatchChildFocus(boolean childIsFocused) {
+        // The host view's background changes when selected, to indicate the focus is inside.
+        setSelected(childIsFocused);
+    }
+}
diff --git a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
index 961799d..50db40f 100644
--- a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
@@ -17,7 +17,6 @@
 package com.android.launcher3.widget;
 
 import android.content.Context;
-import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.PorterDuff;
@@ -34,18 +33,15 @@
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.FastBitmapDrawable;
-import com.android.launcher3.IconCache;
-import com.android.launcher3.IconCache.ItemInfoUpdateReceiver;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
 import com.android.launcher3.ItemInfoWithIcon;
-import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.graphics.DrawableFactory;
 import com.android.launcher3.model.PackageItemInfo;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.util.Themes;
-import com.android.launcher3.widget.LauncherAppWidgetHostView;
 
 public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
         implements OnClickListener, ItemInfoUpdateReceiver {
@@ -137,19 +133,19 @@
             //   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.
-            DrawableFactory drawableFactory = DrawableFactory.get(getContext());
+            DrawableFactory drawableFactory = DrawableFactory.INSTANCE.get(getContext());
             if (mDisabledForSafeMode) {
-                FastBitmapDrawable disabledIcon = drawableFactory.newIcon(info);
+                FastBitmapDrawable disabledIcon = drawableFactory.newIcon(getContext(), info);
                 disabledIcon.setIsDisabled(true);
                 mCenterDrawable = disabledIcon;
                 mSettingIconDrawable = null;
             } else if (isReadyForClickSetup()) {
-                mCenterDrawable = drawableFactory.newIcon(info);
+                mCenterDrawable = drawableFactory.newIcon(getContext(), info);
                 mSettingIconDrawable = getResources().getDrawable(R.drawable.ic_setting).mutate();
                 updateSettingColor(info.iconColor);
             } else {
-                mCenterDrawable = DrawableFactory.get(getContext())
-                        .newPendingIcon(info, getContext());
+                mCenterDrawable = DrawableFactory.INSTANCE.get(getContext())
+                        .newPendingIcon(getContext(), info);
                 mSettingIconDrawable = null;
                 applyState();
             }
diff --git a/src/com/android/launcher3/widget/PendingItemDragHelper.java b/src/com/android/launcher3/widget/PendingItemDragHelper.java
index 74ab14f..8ea9bd4 100644
--- a/src/com/android/launcher3/widget/PendingItemDragHelper.java
+++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java
@@ -34,7 +34,7 @@
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.dragndrop.LivePreviewWidgetCell;
 import com.android.launcher3.graphics.DragPreviewProvider;
-import com.android.launcher3.graphics.LauncherIcons;
+import com.android.launcher3.icons.LauncherIcons;
 
 /**
  * Extension of {@link DragPreviewProvider} with logic specific to pending widgets/shortcuts
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 2ba55ab..dc4af8c 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -26,6 +26,7 @@
 import android.view.View.OnLayoutChangeListener;
 import android.view.ViewGroup;
 import android.view.ViewPropertyAnimator;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
@@ -179,8 +180,8 @@
             return;
         }
         if (bitmap != null) {
-            mWidgetImage.setBitmap(bitmap,
-                    DrawableFactory.get(getContext()).getBadgeForUser(mItem.user, getContext()));
+            mWidgetImage.setBitmap(bitmap, DrawableFactory.INSTANCE.get(getContext())
+                    .getBadgeForUser(mItem.user, getContext()));
             if (mAnimatePreview) {
                 mWidgetImage.setAlpha(0f);
                 ViewPropertyAnimator anim = mWidgetImage.animate();
@@ -236,4 +237,10 @@
     public CharSequence getAccessibilityClassName() {
         return WidgetCell.class.getName();
     }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK);
+    }
 }
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index 5ce7e04..a7078a2 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -16,10 +16,13 @@
 
 package com.android.launcher3.widget;
 
+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.util.IntProperty;
 import android.util.Pair;
 import android.view.Gravity;
 import android.view.LayoutInflater;
@@ -27,11 +30,13 @@
 import android.view.ViewGroup;
 import android.widget.TextView;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.Insettable;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
+import com.android.launcher3.ResourceUtils;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.util.PackageUserKey;
@@ -43,6 +48,20 @@
  */
 public class WidgetsBottomSheet extends BaseWidgetSheet implements Insettable {
 
+    private static final IntProperty<View> PADDING_BOTTOM =
+            new IntProperty<View>("paddingBottom") {
+                @Override
+                public void setValue(View view, int paddingBottom) {
+                    view.setPadding(view.getPaddingLeft(), view.getPaddingTop(),
+                            view.getPaddingRight(), paddingBottom);
+                }
+
+                @Override
+                public Integer get(View view) {
+                    return view.getPaddingBottom();
+                }
+            };
+
     private static final int DEFAULT_CLOSE_DURATION = 200;
     private ItemInfo mOriginalItemInfo;
     private Rect mInsets;
@@ -70,14 +89,13 @@
                 R.string.widgets_bottom_sheet_title, mOriginalItemInfo.title));
 
         onWidgetsBound();
-
-        mLauncher.getDragLayer().addView(this);
+        attachToContainer();
         mIsOpen = false;
         animateOpen();
     }
 
     @Override
-    protected void onWidgetsBound() {
+    public void onWidgetsBound() {
         List<WidgetItem> widgets = mLauncher.getPopupDataProvider().getWidgetsForPackageUser(
                 new PackageUserKey(
                         mOriginalItemInfo.getTargetComponent().getPackageName(),
@@ -108,7 +126,7 @@
             // 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 = Utilities.pxFromDp(
+            leftPaddingView.getLayoutParams().width = ResourceUtils.pxFromDp(
                     16, getResources().getDisplayMetrics());
             widgetCells.addView(leftPaddingView, 0);
         }
@@ -118,7 +136,7 @@
         LayoutInflater.from(getContext()).inflate(R.layout.widget_list_divider, parent, true);
     }
 
-    private WidgetCell addItemCell(ViewGroup parent) {
+    protected WidgetCell addItemCell(ViewGroup parent) {
         WidgetCell widget = (WidgetCell) LayoutInflater.from(getContext()).inflate(
                 R.layout.widget_cell, parent, false);
 
@@ -159,8 +177,7 @@
         int rightInset = insets.right - mInsets.right;
         int bottomInset = insets.bottom - mInsets.bottom;
         mInsets.set(insets);
-        setPadding(getPaddingLeft() + leftInset, getPaddingTop(),
-                getPaddingRight() + rightInset, getPaddingBottom() + bottomInset);
+        setPadding(leftInset, getPaddingTop(), rightInset, bottomInset);
     }
 
     @Override
@@ -173,4 +190,10 @@
         return Pair.create(findViewById(R.id.title),  getContext().getString(
                 mIsOpen ? R.string.widgets_list : R.string.widgets_list_closed));
     }
+
+    @Nullable
+    @Override
+    public Animator createHintCloseAnim(float distanceToMove) {
+        return ObjectAnimator.ofInt(this, PADDING_BOTTOM, (int) (distanceToMove + mInsets.bottom));
+    }
 }
diff --git a/src/com/android/launcher3/widget/WidgetsDiffReporter.java b/src/com/android/launcher3/widget/WidgetsDiffReporter.java
index d67f403..435125b 100644
--- a/src/com/android/launcher3/widget/WidgetsDiffReporter.java
+++ b/src/com/android/launcher3/widget/WidgetsDiffReporter.java
@@ -16,16 +16,17 @@
 
 package com.android.launcher3.widget;
 
-import android.support.v7.widget.RecyclerView;
 import android.util.Log;
 
-import com.android.launcher3.IconCache;
+import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.model.PackageItemInfo;
 import com.android.launcher3.widget.WidgetsListAdapter.WidgetListRowEntryComparator;
 
 import java.util.ArrayList;
 import java.util.Iterator;
 
+import androidx.recyclerview.widget.RecyclerView;
+
 /**
  * Do diff on widget's tray list items and call the {@link RecyclerView.Adapter}
  * methods accordingly.
diff --git a/src/com/android/launcher3/widget/WidgetsFullSheet.java b/src/com/android/launcher3/widget/WidgetsFullSheet.java
index e94d81d..521f511 100644
--- a/src/com/android/launcher3/widget/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsFullSheet.java
@@ -17,6 +17,8 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
 import android.content.Context;
 import android.graphics.Rect;
@@ -27,6 +29,9 @@
 import android.view.View;
 import android.view.animation.AnimationUtils;
 
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
 import com.android.launcher3.Insettable;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
@@ -153,13 +158,13 @@
     }
 
     @Override
-    protected void onWidgetsBound() {
+    public void onWidgetsBound() {
         mAdapter.setWidgets(mLauncher.getPopupDataProvider().getAllWidgets());
     }
 
     private void open(boolean animate) {
         if (animate) {
-            if (mLauncher.getDragLayer().getInsets().bottom > 0) {
+            if (getPopupContainer().getInsets().bottom > 0) {
                 mContent.setAlpha(0);
                 setTranslationShift(VERTICAL_START_POSITION);
             }
@@ -206,10 +211,10 @@
             mNoIntercept = false;
             RecyclerViewFastScroller scroller = mRecyclerView.getScrollbar();
             if (scroller.getThumbOffsetY() >= 0 &&
-                    mLauncher.getDragLayer().isEventOverView(scroller, ev)) {
+                    getPopupContainer().isEventOverView(scroller, ev)) {
                 mNoIntercept = true;
-            } else if (mLauncher.getDragLayer().isEventOverView(mContent, ev)) {
-                mNoIntercept = !mRecyclerView.shouldContainerScroll(ev, mLauncher.getDragLayer());
+            } else if (getPopupContainer().isEventOverView(mContent, ev)) {
+                mNoIntercept = !mRecyclerView.shouldContainerScroll(ev, getPopupContainer());
             }
         }
         return super.onControllerInterceptTouchEvent(ev);
@@ -218,14 +223,28 @@
     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;
-        launcher.getDragLayer().addView(sheet);
         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();
     }
+
+    @Nullable
+    @Override
+    public Animator createHintCloseAnim(float distanceToMove) {
+        AnimatorSet anim = new AnimatorSet();
+        anim.play(ObjectAnimator.ofFloat(mRecyclerView, TRANSLATION_Y, -distanceToMove));
+        anim.play(ObjectAnimator.ofFloat(mRecyclerView, ALPHA, 0.5f));
+        return anim;
+    }
 }
diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java
index 0147ea4..a45521d 100644
--- a/src/com/android/launcher3/widget/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java
@@ -16,8 +16,6 @@
 package com.android.launcher3.widget;
 
 import android.content.Context;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.RecyclerView.Adapter;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -25,19 +23,20 @@
 import android.view.View.OnLongClickListener;
 import android.view.ViewGroup;
 
-import com.android.launcher3.IconCache;
+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 com.android.launcher3.util.PackageUserKey;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.Iterator;
 import java.util.List;
 
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.Adapter;
+
 /**
  * List view adapter for the widget tray.
  *
diff --git a/src/com/android/launcher3/widget/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/WidgetsRecyclerView.java
index 124058e..c15557b 100644
--- a/src/com/android/launcher3/widget/WidgetsRecyclerView.java
+++ b/src/com/android/launcher3/widget/WidgetsRecyclerView.java
@@ -18,9 +18,6 @@
 
 import android.content.Context;
 import android.graphics.Point;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.RecyclerView.OnItemTouchListener;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.View;
@@ -28,6 +25,10 @@
 import com.android.launcher3.BaseRecyclerView;
 import com.android.launcher3.R;
 
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.OnItemTouchListener;
+
 /**
  * The widgets recycler view.
  */
@@ -55,11 +56,6 @@
         addOnItemTouchListener(this);
     }
 
-    public WidgetsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
-        this(context, attrs, defStyleAttr);
-    }
-
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
diff --git a/src/com/android/launcher3/widget/WidgetsRowViewHolder.java b/src/com/android/launcher3/widget/WidgetsRowViewHolder.java
index 8f269a6..d26edb6 100644
--- a/src/com/android/launcher3/widget/WidgetsRowViewHolder.java
+++ b/src/com/android/launcher3/widget/WidgetsRowViewHolder.java
@@ -15,12 +15,13 @@
  */
 package com.android.launcher3.widget;
 
-import android.support.v7.widget.RecyclerView.ViewHolder;
 import android.view.ViewGroup;
 
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.R;
 
+import androidx.recyclerview.widget.RecyclerView.ViewHolder;
+
 public class WidgetsRowViewHolder extends ViewHolder {
 
     public final ViewGroup cellContainer;
diff --git a/src_build_config/BuildConfig.java b/src_build_config/BuildConfig.java
new file mode 100644
index 0000000..36d7f4b
--- /dev/null
+++ b/src_build_config/BuildConfig.java
@@ -0,0 +1,21 @@
+/*
+ * 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;
+
+public final class BuildConfig {
+  public static final String APPLICATION_ID = "com.android.launcher3";
+}
diff --git a/src_flags/com/android/launcher3/config/FeatureFlags.java b/src_flags/com/android/launcher3/config/FeatureFlags.java
index 3ffb6c9..73c6996 100644
--- a/src_flags/com/android/launcher3/config/FeatureFlags.java
+++ b/src_flags/com/android/launcher3/config/FeatureFlags.java
@@ -16,10 +16,13 @@
 
 package com.android.launcher3.config;
 
+import android.content.Context;
+
 /**
  * Defines a set of flags used to control various launcher behaviors
  */
 public final class FeatureFlags extends BaseFlags {
-
-    private FeatureFlags() {}
+    private FeatureFlags() {
+        // Prevent instantiation
+    }
 }
diff --git a/src_plugins/README.md b/src_plugins/README.md
new file mode 100644
index 0000000..c7ce1da
--- /dev/null
+++ b/src_plugins/README.md
@@ -0,0 +1,3 @@
+This directory contains plugin interfaces that launcher listens for and plugins implement. In other words, these are the hooks that specify what plugins launcher currently supports. 
+
+Details about how to create a new plugin interface, or to use existing interfaces to write a plugin can be found at go/gnl/plugins.
diff --git a/src_plugins/com/android/systemui/plugins/AllAppsRow.java b/src_plugins/com/android/systemui/plugins/AllAppsRow.java
new file mode 100644
index 0000000..c003fc1
--- /dev/null
+++ b/src_plugins/com/android/systemui/plugins/AllAppsRow.java
@@ -0,0 +1,51 @@
+/*
+ * 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.systemui.plugins;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+/**
+ * Implement this plugin interface to add a row of views to the top of the all apps drawer.
+ */
+@ProvidesInterface(action = AllAppsRow.ACTION, version = AllAppsRow.VERSION)
+public interface AllAppsRow extends Plugin {
+    String ACTION = "com.android.systemui.action.PLUGIN_ALL_APPS_ACTIONS";
+    int VERSION = 1;
+
+    /**
+     * Setup the row and return the parent view.
+     * @param parent The ViewGroup to which launcher will add this row.
+     */
+    View setup(ViewGroup parent);
+
+    /**
+     * @return The height to reserve in all apps for your views.
+     */
+    int getExpectedHeight();
+
+    /**
+     * Update launcher whenever {@link #getExpectedHeight()} changes.
+     */
+    void setOnHeightUpdatedListener(OnHeightUpdatedListener onHeightUpdatedListener);
+
+    interface OnHeightUpdatedListener {
+        void onHeightUpdated();
+    }
+}
diff --git a/src_plugins/com/android/systemui/plugins/FirstScreenWidget.java b/src_plugins/com/android/systemui/plugins/FirstScreenWidget.java
new file mode 100644
index 0000000..8d7dd4b
--- /dev/null
+++ b/src_plugins/com/android/systemui/plugins/FirstScreenWidget.java
@@ -0,0 +1,15 @@
+package com.android.systemui.plugins;
+
+import android.view.ViewGroup;
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+/**
+ * Implement this interface to wrap the widget on the first home screen, e.g. to add new content.
+ */
+@ProvidesInterface(action = FirstScreenWidget.ACTION, version = FirstScreenWidget.VERSION)
+public interface FirstScreenWidget extends Plugin {
+    String ACTION = "com.android.systemui.action.PLUGIN_FIRST_SCREEN_WIDGET";
+    int VERSION = 1;
+
+    void onWidgetUpdated(ViewGroup widgetView);
+}
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java b/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java
new file mode 100644
index 0000000..1710aef
--- /dev/null
+++ b/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java
@@ -0,0 +1,54 @@
+/*
+ * 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;
+
+import com.android.launcher3.AllAppsList;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel.Callbacks;
+import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.widget.WidgetListRowEntry;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * Helper class to handle results of {@link com.android.launcher3.model.LoaderTask}.
+ */
+public class LoaderResults extends BaseLoaderResults {
+
+    public LoaderResults(LauncherAppState app, BgDataModel dataModel,
+            AllAppsList allAppsList, int pageToBindFirst, WeakReference<Callbacks> callbacks) {
+        super(app, dataModel, allAppsList, pageToBindFirst, callbacks);
+    }
+
+    @Override
+    public void bindDeepShortcuts() {
+        final HashMap<ComponentKey, Integer> shortcutMapCopy;
+        synchronized (mBgDataModel) {
+            shortcutMapCopy = new HashMap<>(mBgDataModel.deepShortcutMap);
+        }
+        executeCallbacksTask(c -> c.bindDeepShortcutMap(shortcutMapCopy), mUiExecutor);
+    }
+
+    @Override
+    public void bindWidgets() {
+        final ArrayList<WidgetListRowEntry> widgets =
+                mBgDataModel.widgetsModel.getWidgetsList(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
new file mode 100644
index 0000000..7a7f828
--- /dev/null
+++ b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
@@ -0,0 +1,241 @@
+
+package com.android.launcher3.model;
+
+import static android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_HIDE_FROM_PICKER;
+
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Process;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.launcher3.AppFilter;
+import com.android.launcher3.icons.ComponentWithLabel;
+import com.android.launcher3.icons.IconCache;
+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.compat.AppWidgetManagerCompat;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.ShortcutConfigActivityInfo;
+import com.android.launcher3.config.FeatureFlags;
+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 java.util.ArrayList;
+import java.util.Collections;
+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 androidx.annotation.Nullable;
+
+/**
+ * Widgets data model that is used by the adapters of the widget views and controllers.
+ *
+ * <p> The widgets and shortcuts are organized using package name as its index.
+ */
+public class WidgetsModel {
+
+    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 AppFilter mAppFilter;
+
+    /**
+     * 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}
+     *
+     * @see com.android.launcher3.widget.WidgetsListAdapter#setWidgets(ArrayList)
+     */
+    public synchronized ArrayList<WidgetListRowEntry> getWidgetsList(Context context) {
+        ArrayList<WidgetListRowEntry> 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);
+        }
+        return result;
+    }
+
+    /**
+     * @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise
+     *                    only widgets and shortcuts associated with the package/user are.
+     */
+    public List<ComponentWithLabel> update(LauncherAppState app, @Nullable PackageUserKey packageUser) {
+        Preconditions.assertWorkerThread();
+
+        Context context = app.getContext();
+        final ArrayList<WidgetItem> widgetsAndShortcuts = new ArrayList<>();
+        List<ComponentWithLabel> updatedItems = new ArrayList<>();
+        try {
+            InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
+            PackageManager pm = app.getContext().getPackageManager();
+
+            // Widgets
+            AppWidgetManagerCompat widgetManager = AppWidgetManagerCompat.getInstance(context);
+            for (AppWidgetProviderInfo widgetInfo : widgetManager.getAllProviders(packageUser)) {
+                LauncherAppWidgetProviderInfo launcherWidgetInfo =
+                        LauncherAppWidgetProviderInfo.fromProviderInfo(context, widgetInfo);
+
+                widgetsAndShortcuts.add(new WidgetItem(
+                        launcherWidgetInfo, idp, app.getIconCache()));
+                updatedItems.add(launcherWidgetInfo);
+            }
+
+            // Shortcuts
+            for (ShortcutConfigActivityInfo info : LauncherAppsCompat.getInstance(context)
+                    .getCustomShortcutActivityList(packageUser)) {
+                widgetsAndShortcuts.add(new WidgetItem(info, app.getIconCache(), pm));
+                updatedItems.add(info);
+            }
+            setWidgetsAndShortcuts(widgetsAndShortcuts, app, packageUser);
+        } catch (Exception e) {
+            if (!FeatureFlags.IS_DOGFOOD_BUILD && Utilities.isBinderSizeError(e)) {
+                // the returned value may be incomplete and will not be refreshed until the next
+                // time Launcher starts.
+                // TODO: after figuring out a repro step, introduce a dirty bit to check when
+                // onResume is called to refresh the widget provider list.
+            } else {
+                throw e;
+            }
+        }
+
+        app.getWidgetCache().removeObsoletePreviews(widgetsAndShortcuts, packageUser);
+        return updatedItems;
+    }
+
+    private synchronized void setWidgetsAndShortcuts(ArrayList<WidgetItem> rawWidgetsShortcuts,
+            LauncherAppState app, @Nullable PackageUserKey packageUser) {
+        if (DEBUG) {
+            Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + rawWidgetsShortcuts.size());
+        }
+
+        // Temporary list 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<>();
+
+        // clear the lists.
+        if (packageUser == null) {
+            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();
+                    }
+                }
+            }
+        }
+
+        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);
+        }
+
+        // Update each package entry
+        IconCache iconCache = app.getIconCache();
+        for (PackageItemInfo p : tmpPackageItemInfos.values()) {
+            iconCache.getTitleAndIconForApp(p, true /* userLowResIcon */);
+        }
+    }
+
+    public void onPackageIconsUpdated(Set<String> packageNames, UserHandle user,
+            LauncherAppState app) {
+        for (Entry<PackageItemInfo, ArrayList<WidgetItem>> entry : mWidgetsList.entrySet()) {
+            if (packageNames.contains(entry.getKey().packageName)) {
+                ArrayList<WidgetItem> items = entry.getValue();
+                int count = items.size();
+                for (int i = 0; i < count; i++) {
+                    WidgetItem item = items.get(i);
+                    if (item.user.equals(user)) {
+                        if (item.activityInfo != null) {
+                            items.set(i, new WidgetItem(item.activityInfo, app.getIconCache(),
+                                    app.getContext().getPackageManager()));
+                        } else {
+                            items.set(i, new WidgetItem(item.widgetInfo,
+                                    app.getInvariantDeviceProfile(), app.getIconCache()));
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java b/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java
new file mode 100644
index 0000000..6b6f70d
--- /dev/null
+++ b/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java
@@ -0,0 +1,223 @@
+/*
+ * 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.shortcuts;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.LauncherApps;
+import android.content.pm.LauncherApps.ShortcutQuery;
+import android.content.pm.ShortcutInfo;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.WorkspaceItemInfo;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Performs operations related to deep shortcuts, such as querying for them, pinning them, etc.
+ */
+public class DeepShortcutManager {
+    private static final String TAG = "DeepShortcutManager";
+
+    private static final int FLAG_GET_ALL = ShortcutQuery.FLAG_MATCH_DYNAMIC
+            | ShortcutQuery.FLAG_MATCH_MANIFEST | ShortcutQuery.FLAG_MATCH_PINNED;
+
+    private static DeepShortcutManager sInstance;
+    private static final Object sInstanceLock = new Object();
+
+    public static DeepShortcutManager getInstance(Context context) {
+        synchronized (sInstanceLock) {
+            if (sInstance == null) {
+                sInstance = new DeepShortcutManager(context.getApplicationContext());
+            }
+            return sInstance;
+        }
+    }
+
+    private final LauncherApps mLauncherApps;
+    private boolean mWasLastCallSuccess;
+
+    private DeepShortcutManager(Context context) {
+        mLauncherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
+    }
+
+    public static boolean supportsShortcuts(ItemInfo info) {
+        boolean isItemPromise = info instanceof WorkspaceItemInfo
+                && ((WorkspaceItemInfo) info).hasPromiseIconUi();
+        return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
+                && !info.isDisabled() && !isItemPromise;
+    }
+
+    public boolean wasLastCallSuccess() {
+        return mWasLastCallSuccess;
+    }
+
+    /**
+     * Queries for the shortcuts with the package name and provided ids.
+     *
+     * This method is intended to get the full details for shortcuts when they are added or updated,
+     * because we only get "key" fields in onShortcutsChanged().
+     */
+    public List<ShortcutInfo> queryForFullDetails(String packageName,
+            List<String> shortcutIds, UserHandle user) {
+        return query(FLAG_GET_ALL, packageName, null, shortcutIds, user);
+    }
+
+    /**
+     * Gets all the manifest and dynamic shortcuts associated with the given package and user,
+     * to be displayed in the shortcuts container on long press.
+     */
+    public List<ShortcutInfo> queryForShortcutsContainer(ComponentName activity,
+            UserHandle user) {
+        return query(ShortcutQuery.FLAG_MATCH_MANIFEST | ShortcutQuery.FLAG_MATCH_DYNAMIC,
+                activity.getPackageName(), activity, null, user);
+    }
+
+    /**
+     * Removes the given shortcut from the current list of pinned shortcuts.
+     * (Runs on background thread)
+     */
+    public void unpinShortcut(final ShortcutKey key) {
+        String packageName = key.componentName.getPackageName();
+        String id = key.getId();
+        UserHandle user = key.user;
+        List<String> pinnedIds = extractIds(queryForPinnedShortcuts(packageName, user));
+        pinnedIds.remove(id);
+        try {
+            mLauncherApps.pinShortcuts(packageName, pinnedIds, user);
+            mWasLastCallSuccess = true;
+        } catch (SecurityException|IllegalStateException e) {
+            Log.w(TAG, "Failed to unpin shortcut", e);
+            mWasLastCallSuccess = false;
+        }
+    }
+
+    /**
+     * Adds the given shortcut to the current list of pinned shortcuts.
+     * (Runs on background thread)
+     */
+    public void pinShortcut(final ShortcutKey key) {
+        String packageName = key.componentName.getPackageName();
+        String id = key.getId();
+        UserHandle user = key.user;
+        List<String> pinnedIds = extractIds(queryForPinnedShortcuts(packageName, user));
+        pinnedIds.add(id);
+        try {
+            mLauncherApps.pinShortcuts(packageName, pinnedIds, user);
+            mWasLastCallSuccess = true;
+        } catch (SecurityException|IllegalStateException e) {
+            Log.w(TAG, "Failed to pin shortcut", e);
+            mWasLastCallSuccess = false;
+        }
+    }
+
+    public void startShortcut(String packageName, String id, Rect sourceBounds,
+          Bundle startActivityOptions, UserHandle user) {
+        try {
+            mLauncherApps.startShortcut(packageName, id, sourceBounds,
+                    startActivityOptions, user);
+            mWasLastCallSuccess = true;
+        } catch (SecurityException|IllegalStateException e) {
+            Log.e(TAG, "Failed to start shortcut", e);
+            mWasLastCallSuccess = false;
+        }
+    }
+
+    public Drawable getShortcutIconDrawable(ShortcutInfo shortcutInfo, int density) {
+        try {
+            Drawable icon = mLauncherApps.getShortcutIconDrawable(shortcutInfo, density);
+            mWasLastCallSuccess = true;
+            return icon;
+        } catch (SecurityException|IllegalStateException e) {
+            Log.e(TAG, "Failed to get shortcut icon", e);
+            mWasLastCallSuccess = false;
+        }
+        return null;
+    }
+
+    /**
+     * Returns the id's of pinned shortcuts associated with the given package and user.
+     *
+     * If packageName is null, returns all pinned shortcuts regardless of package.
+     */
+    public List<ShortcutInfo> queryForPinnedShortcuts(String packageName, UserHandle user) {
+        return queryForPinnedShortcuts(packageName, null, user);
+    }
+
+    public List<ShortcutInfo> queryForPinnedShortcuts(String packageName,
+            List<String> shortcutIds, UserHandle user) {
+        return query(ShortcutQuery.FLAG_MATCH_PINNED, packageName, null, shortcutIds, user);
+    }
+
+    public List<ShortcutInfo> queryForAllShortcuts(UserHandle user) {
+        return query(FLAG_GET_ALL, null, null, null, user);
+    }
+
+    private List<String> extractIds(List<ShortcutInfo> shortcuts) {
+        List<String> shortcutIds = new ArrayList<>(shortcuts.size());
+        for (ShortcutInfo shortcut : shortcuts) {
+            shortcutIds.add(shortcut.getId());
+        }
+        return shortcutIds;
+    }
+
+    /**
+     * Query the system server for all the shortcuts matching the given parameters.
+     * If packageName == null, we query for all shortcuts with the passed flags, regardless of app.
+     *
+     * TODO: Use the cache to optimize this so we don't make an RPC every time.
+     */
+    private List<ShortcutInfo> query(int flags, String packageName,
+            ComponentName activity, List<String> shortcutIds, UserHandle user) {
+        ShortcutQuery q = new ShortcutQuery();
+        q.setQueryFlags(flags);
+        if (packageName != null) {
+            q.setPackage(packageName);
+            q.setActivity(activity);
+            q.setShortcutIds(shortcutIds);
+        }
+        List<ShortcutInfo> shortcutInfos = null;
+        try {
+            shortcutInfos = mLauncherApps.getShortcuts(q, user);
+            mWasLastCallSuccess = true;
+        } catch (SecurityException|IllegalStateException e) {
+            Log.e(TAG, "Failed to query for shortcuts", e);
+            mWasLastCallSuccess = false;
+        }
+        if (shortcutInfos == null) {
+            return Collections.EMPTY_LIST;
+        }
+        return shortcutInfos;
+    }
+
+    public boolean hasHostPermission() {
+        try {
+            return mLauncherApps.hasShortcutHostPermission();
+        } catch (SecurityException|IllegalStateException e) {
+            Log.e(TAG, "Failed to make shortcut manager call", e);
+        }
+        return false;
+    }
+}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsState.java b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsState.java
deleted file mode 100644
index f7bb254..0000000
--- a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsState.java
+++ /dev/null
@@ -1,83 +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;
-
-import static com.android.launcher3.LauncherAnimUtils.ALL_APPS_TRANSITION_MS;
-import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN;
-import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.R;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-
-/**
- * Definition for AllApps state
- */
-public class AllAppsState extends LauncherState {
-
-    private static final float PARALLAX_COEFFICIENT = .125f;
-
-    private static final int STATE_FLAGS = FLAG_DISABLE_ACCESSIBILITY;
-
-    private static final PageAlphaProvider PAGE_ALPHA_PROVIDER = new PageAlphaProvider(DEACCEL_2) {
-        @Override
-        public float getPageAlpha(int pageIndex) {
-            return 0;
-        }
-    };
-
-    public AllAppsState(int id) {
-        super(id, ContainerType.ALLAPPS, ALL_APPS_TRANSITION_MS, STATE_FLAGS);
-    }
-
-    @Override
-    public void onStateEnabled(Launcher launcher) {
-        if (!launcher.getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)) {
-            launcher.getSharedPrefs().edit().putBoolean(HOME_BOUNCE_SEEN, true).apply();
-        }
-
-        AbstractFloatingView.closeAllOpenViews(launcher);
-        dispatchWindowStateChanged(launcher);
-    }
-
-    @Override
-    public String getDescription(Launcher launcher) {
-        return launcher.getString(R.string.all_apps_button_label);
-    }
-
-    @Override
-    public int getVisibleElements(Launcher launcher) {
-        return ALL_APPS_HEADER | ALL_APPS_CONTENT;
-    }
-
-    @Override
-    public float[] getWorkspaceScaleAndTranslation(Launcher launcher) {
-        return new float[] { 1f, 0,
-                -launcher.getAllAppsController().getShiftRange() * PARALLAX_COEFFICIENT};
-    }
-
-    @Override
-    public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
-        return PAGE_ALPHA_PROVIDER;
-    }
-
-    @Override
-    public float getVerticalProgress(Launcher launcher) {
-        return 0f;
-    }
-}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/FastOverviewState.java b/src_ui_overrides/com/android/launcher3/uioverrides/FastOverviewState.java
deleted file mode 100644
index 147d194..0000000
--- a/src_ui_overrides/com/android/launcher3/uioverrides/FastOverviewState.java
+++ /dev/null
@@ -1,26 +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;
-
-/**
- * A dummy overview state
- */
-public class FastOverviewState extends OverviewState {
-
-    public FastOverviewState(int id) {
-        super(id);
-    }
-}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java
deleted file mode 100644
index 8def0d3..0000000
--- a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java
+++ /dev/null
@@ -1,31 +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;
-
-import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
-
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-
-/**
- * Definition for overview state
- */
-public class OverviewState extends LauncherState {
-
-    public OverviewState(int id) {
-        super(id, ContainerType.WORKSPACE, OVERVIEW_TRANSITION_MS, FLAG_DISABLE_RESTORE);
-    }
-}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
index 0d727fd..5cc64dc 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
@@ -18,10 +18,16 @@
 
 import android.app.Activity;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.os.Bundle;
 import android.os.CancellationSignal;
 
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState.ScaleAndTranslation;
 import com.android.launcher3.LauncherStateManager.StateHandler;
+import com.android.launcher3.graphics.RotationMode;
 import com.android.launcher3.util.TouchController;
 
 import java.io.PrintWriter;
@@ -33,7 +39,9 @@
                 launcher.getDragController(), new AllAppsSwipeController(launcher)};
     }
 
-    public static void setOnTouchControllersChangedListener(Context context, Runnable listener) { }
+    public static Runnable enableLiveUIChanges(Launcher l) {
+        return null;
+    }
 
     public static StateHandler[] getStateHandler(Launcher launcher) {
         return new StateHandler[] {
@@ -61,7 +69,30 @@
         return false;
     }
 
-    public static void prepareToShowOverview(Launcher launcher) { }
-
     public static void setBackButtonAlpha(Launcher launcher, float alpha, boolean animate) { }
+
+
+    public static ScaleAndTranslation getOverviewScaleAndTranslationForNormalState(Launcher l) {
+        return new ScaleAndTranslation(1.1f, 0f, 0f);
+    }
+
+    public static RotationMode getRotationMode(DeviceProfile dp) {
+        return RotationMode.NORMAL;
+    }
+
+    public static boolean startIntentSenderForResult(Activity activity, IntentSender intent,
+            int requestCode, Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags,
+            Bundle options) {
+        return false;
+    }
+
+    public static boolean startActivityForResult(Activity activity, Intent intent, int requestCode,
+            Bundle options) {
+        return false;
+    }
+
+    public static void resetPendingActivityResults(Launcher launcher, int requestCode) { }
+
+    public static void clearSwipeSharedState(boolean finishAnimation) {}
+
 }
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/WallpaperColorInfo.java b/src_ui_overrides/com/android/launcher3/uioverrides/WallpaperColorInfo.java
index 2107094..b05e125 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/WallpaperColorInfo.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/WallpaperColorInfo.java
@@ -30,6 +30,10 @@
 
 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;
     private static final Object sInstanceLock = new Object();
     private static WallpaperColorInfo sInstance;
@@ -56,7 +60,7 @@
     private WallpaperColorInfo(Context context) {
         mWallpaperManager = WallpaperManagerCompat.getInstance(context);
         mWallpaperManager.addOnColorsChangedListener(this);
-        mExtractionType = ColorExtractionAlgorithm.newInstance(context);
+        mExtractionType = new ColorExtractionAlgorithm();
         update(mWallpaperManager.getWallpaperColors(FLAG_SYSTEM));
     }
 
@@ -76,6 +80,10 @@
         return mSupportsDarkText;
     }
 
+    public boolean isMainColorDark() {
+        return mMainColor == MAIN_COLOR_DARK;
+    }
+
     @Override
     public void onColorsChanged(WallpaperColorsCompat colors, int which) {
         if ((which & FLAG_SYSTEM) != 0) {
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/ColorExtractionAlgorithm.java b/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/ColorExtractionAlgorithm.java
index 0444212..5a1f9ca 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/ColorExtractionAlgorithm.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/ColorExtractionAlgorithm.java
@@ -16,32 +16,26 @@
 
 package com.android.launcher3.uioverrides.dynamicui;
 
-import android.content.Context;
 import android.graphics.Color;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v4.graphics.ColorUtils;
 import android.util.Log;
 import android.util.Pair;
 import android.util.Range;
 
-import com.android.launcher3.R;
 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 {
 
-    public static ColorExtractionAlgorithm newInstance(Context context) {
-        return Utilities.getOverrideObject(ColorExtractionAlgorithm.class,
-                context.getApplicationContext(), R.string.color_extraction_impl_class);
-    }
-
     private static final String TAG = "Tonal";
 
     // Used for tonal palette fitting
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperManagerCompat.java b/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperManagerCompat.java
index 5c533ff..0fd0a35 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperManagerCompat.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperManagerCompat.java
@@ -17,10 +17,11 @@
 package com.android.launcher3.uioverrides.dynamicui;
 
 import android.content.Context;
-import android.support.annotation.Nullable;
 
 import com.android.launcher3.Utilities;
 
+import androidx.annotation.Nullable;
+
 public abstract class WallpaperManagerCompat {
 
     private static final Object sInstanceLock = new Object();
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperManagerCompatVL.java b/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperManagerCompatVL.java
index 4a8bbbd..500fdc3 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperManagerCompatVL.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperManagerCompatVL.java
@@ -18,7 +18,6 @@
 import static android.app.WallpaperManager.FLAG_SYSTEM;
 
 import static com.android.launcher3.Utilities.getDevicePrefs;
-import static com.android.launcher3.graphics.ColorExtractor.findDominantColorByHue;
 
 import android.app.WallpaperInfo;
 import android.app.WallpaperManager;
@@ -42,15 +41,16 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.ParcelFileDescriptor;
-import android.support.annotation.Nullable;
 import android.util.Log;
 import android.util.Pair;
 
-import com.android.launcher3.Utilities;
+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";
@@ -60,6 +60,8 @@
     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;
@@ -121,7 +123,7 @@
     }
 
     private void reloadColors() {
-        JobInfo job = new JobInfo.Builder(Utilities.WALLPAPER_COMPAT_JOB_ID,
+        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);
@@ -136,9 +138,6 @@
     }
 
     private static final int getWallpaperId(Context context) {
-        if (!Utilities.ATLEAST_NOUGAT) {
-            return -1;
-        }
         return context.getSystemService(WallpaperManager.class).getWallpaperId(FLAG_SYSTEM);
     }
 
@@ -169,6 +168,7 @@
 
         private HandlerThread mWorkerThread;
         private Handler mWorkerHandler;
+        private ColorExtractor mColorExtractor;
 
         @Override
         public void onCreate() {
@@ -176,6 +176,7 @@
             mWorkerThread = new HandlerThread("ColorExtractionService");
             mWorkerThread.start();
             mWorkerHandler = new Handler(mWorkerThread.getLooper());
+            mColorExtractor = new ColorExtractor();
         }
 
         @Override
@@ -212,27 +213,25 @@
                 // For live wallpaper, extract colors from thumbnail
                 drawable = info.loadThumbnail(getPackageManager());
             } else {
-                if (Utilities.ATLEAST_NOUGAT) {
-                    try (ParcelFileDescriptor fd = wm.getWallpaperFile(FLAG_SYSTEM)) {
-                        BitmapRegionDecoder decoder = BitmapRegionDecoder
-                                .newInstance(fd.getFileDescriptor(), false);
+                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();
+                    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 (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();
@@ -258,7 +257,8 @@
             String value = VERSION_PREFIX + wallpaperId;
 
             if (bitmap != null) {
-                int color = findDominantColorByHue(bitmap, MAX_WALLPAPER_EXTRACTION_AREA);
+                int color = mColorExtractor.findDominantColorByHue(bitmap,
+                        MAX_WALLPAPER_EXTRACTION_AREA);
                 value += "," + color;
             }
 
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperManagerCompatVOMR1.java b/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperManagerCompatVOMR1.java
index 4509e05..f34497d 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperManagerCompatVOMR1.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperManagerCompatVOMR1.java
@@ -21,11 +21,12 @@
 import android.app.WallpaperManager.OnColorsChangedListener;
 import android.content.Context;
 import android.graphics.Color;
-import android.support.annotation.Nullable;
 import android.util.Log;
 
 import java.lang.reflect.Method;
 
+import androidx.annotation.Nullable;
+
 @TargetApi(27)
 public class WallpaperManagerCompatVOMR1 extends WallpaperManagerCompat {
 
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java b/src_ui_overrides/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
new file mode 100644
index 0000000..e1a35c9
--- /dev/null
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.launcher3.uioverrides.plugins;
+
+import android.content.ComponentName;
+import android.content.Context;
+
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.systemui.plugins.Plugin;
+import com.android.systemui.plugins.PluginListener;
+
+import java.util.Collections;
+import java.util.Set;
+
+import androidx.preference.PreferenceDataStore;
+
+public class PluginManagerWrapper {
+
+    public static final MainThreadInitializedObject<PluginManagerWrapper> INSTANCE =
+            new MainThreadInitializedObject<>(PluginManagerWrapper::new);
+
+    private static final String PREFIX_PLUGIN_ENABLED = "PLUGIN_ENABLED_";
+    public static final String PLUGIN_CHANGED = "com.android.systemui.action.PLUGIN_CHANGED";
+
+    private PluginManagerWrapper(Context c) {
+    }
+
+    public void addPluginListener(PluginListener<? extends Plugin> listener, Class<?> pluginClass) {
+    }
+
+    public void addPluginListener(PluginListener<? extends Plugin> listener, Class<?> pluginClass,
+            boolean allowMultiple) {
+    }
+
+    public void removePluginListener(PluginListener<? extends Plugin> listener) { }
+
+    public Set<String> getPluginActions() {
+        return Collections.emptySet();
+    }
+
+    public PreferenceDataStore getPluginEnabler() {
+        return new PreferenceDataStore() { };
+    }
+
+    public static String pluginEnabledKey(ComponentName cn) {
+        return PREFIX_PLUGIN_ENABLED + cn.flattenToString();
+    }
+
+    public static boolean hasPlugins(Context context) {
+        return false;
+    }
+}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
new file mode 100644
index 0000000..7006d77
--- /dev/null
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -0,0 +1,83 @@
+/*
+ * 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.LauncherAnimUtils.ALL_APPS_TRANSITION_MS;
+import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.R;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+
+/**
+ * Definition for AllApps state
+ */
+public class AllAppsState extends LauncherState {
+
+    private static final float PARALLAX_COEFFICIENT = .125f;
+
+    private static final int STATE_FLAGS = FLAG_DISABLE_ACCESSIBILITY;
+
+    private static final PageAlphaProvider PAGE_ALPHA_PROVIDER = new PageAlphaProvider(DEACCEL_2) {
+        @Override
+        public float getPageAlpha(int pageIndex) {
+            return 0;
+        }
+    };
+
+    public AllAppsState(int id) {
+        super(id, ContainerType.ALLAPPS, ALL_APPS_TRANSITION_MS, STATE_FLAGS);
+    }
+
+    @Override
+    public void onStateEnabled(Launcher launcher) {
+        if (!launcher.getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)) {
+            launcher.getSharedPrefs().edit().putBoolean(HOME_BOUNCE_SEEN, true).apply();
+        }
+
+        AbstractFloatingView.closeAllOpenViews(launcher);
+        dispatchWindowStateChanged(launcher);
+    }
+
+    @Override
+    public String getDescription(Launcher launcher) {
+        return launcher.getString(R.string.all_apps_button_label);
+    }
+
+    @Override
+    public int getVisibleElements(Launcher launcher) {
+        return ALL_APPS_HEADER | ALL_APPS_CONTENT;
+    }
+
+    @Override
+    public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
+        return new ScaleAndTranslation(1f, 0,
+                -launcher.getAllAppsController().getShiftRange() * PARALLAX_COEFFICIENT);
+    }
+
+    @Override
+    public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
+        return PAGE_ALPHA_PROVIDER;
+    }
+
+    @Override
+    public float getVerticalProgress(Launcher launcher) {
+        return 0f;
+    }
+}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java b/src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java
new file mode 100644
index 0000000..aeba788
--- /dev/null
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -0,0 +1,43 @@
+/*
+ * 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.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
+
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+
+/**
+ * Definition for overview state
+ */
+public class OverviewState extends LauncherState {
+
+    public OverviewState(int id) {
+        super(id, ContainerType.WORKSPACE, OVERVIEW_TRANSITION_MS, FLAG_DISABLE_RESTORE);
+    }
+
+    public static OverviewState newBackgroundState(int id) {
+        return new OverviewState(id);
+    }
+
+    public static OverviewState newPeekState(int id) {
+        return new OverviewState(id);
+    }
+
+    public static OverviewState newSwitchState(int id) {
+        return new OverviewState(id);
+    }
+}
diff --git a/tests/Android.mk b/tests/Android.mk
index f6f02fe..0c41241 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -14,19 +14,60 @@
 #
 
 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 += libSharedSystemUI
+
+    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_SDK_VERSION := current
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+#
+# Build rule for Launcher3Tests
+#
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := tests
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test ub-uiautomator mockito-target-minus-junit4
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    androidx.test.runner \
+    androidx.test.rules \
+    androidx.test.uiautomator_uiautomator \
+    mockito-target-minus-junit4
+
+ifneq (,$(wildcard frameworks/base))
+    LOCAL_PRIVATE_PLATFORM_APIS := true
+    LOCAL_STATIC_JAVA_LIBRARIES += launcher-aosp-tapl
+else
+    LOCAL_SDK_VERSION := 28
+    LOCAL_MIN_SDK_VERSION := 21
+    LOCAL_STATIC_JAVA_LIBRARIES += ub-launcher-aosp-tapl
+endif
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_FULL_LIBS_MANIFEST_FILES := $(LOCAL_PATH)/AndroidManifest-common.xml
 
-LOCAL_SDK_VERSION := 28
-LOCAL_MIN_SDK_VERSION := 21
-
 LOCAL_PACKAGE_NAME := Launcher3Tests
 
 LOCAL_INSTRUMENTATION_FOR := Launcher3
 
 include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index af8b15c..61c7306 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -18,38 +18,39 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.launcher3.tests">
 
+    <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/>
+
     <application android:debuggable="true">
-        <uses-library android:name="android.test.runner" />
+        <uses-library android:name="android.test.runner"/>
 
         <receiver
             android:name="com.android.launcher3.testcomponent.AppWidgetNoConfig"
             android:label="No Config">
             <intent-filter>
-                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+                <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
             </intent-filter>
             <meta-data android:name="android.appwidget.provider"
-                       android:resource="@xml/appwidget_no_config" />
+                       android:resource="@xml/appwidget_no_config"/>
         </receiver>
 
-
         <receiver
             android:name="com.android.launcher3.testcomponent.AppWdigetHidden"
             android:label="Hidden widget">
             <intent-filter>
-                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+                <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
             </intent-filter>
             <meta-data android:name="android.appwidget.provider"
-                android:resource="@xml/appwidget_hidden" />
+                       android:resource="@xml/appwidget_hidden"/>
         </receiver>
 
         <receiver
             android:name="com.android.launcher3.testcomponent.AppWidgetWithConfig"
             android:label="With Config">
             <intent-filter>
-                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+                <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
             </intent-filter>
             <meta-data android:name="android.appwidget.provider"
-                       android:resource="@xml/appwidget_with_config" />
+                       android:resource="@xml/appwidget_with_config"/>
         </receiver>
 
         <activity
@@ -60,13 +61,136 @@
         </activity>
         <activity
             android:name="com.android.launcher3.testcomponent.RequestPinItemActivity"
-            android:label="Test Pin Item"
-            android:icon="@drawable/test_drawable_pin_item">
+            android:icon="@drawable/test_drawable_pin_item"
+            android:label="Test Pin Item">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
                 <category android:name="android.intent.category.LAUNCHER"/>
                 <category android:name="android.intent.category.DEFAULT"/>
             </intent-filter>
         </activity>
+
+        <provider
+            android:name="com.android.launcher3.testcomponent.TestCommandReceiver"
+            android:authorities="${packageName}.commands"
+            android:exported="true"/>
+
+        <activity
+            android:name="com.android.launcher3.testcomponent.TestLauncherActivity"
+            android:clearTaskOnLaunch="true"
+            android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
+            android:enabled="false"
+            android:label="Test launcher"
+            android:launchMode="singleTask"
+            android:process=":testLauncherProcess"
+            android:resizeableActivity="true"
+            android:screenOrientation="unspecified"
+            android:stateNotNeeded="true"
+            android:taskAffinity=""
+            android:theme="@android:style/Theme.DeviceDefault.Light"
+            android:windowSoftInputMode="adjustPan">
+            <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>
+        </activity>
+        <activity
+            android:name="com.android.launcher3.testcomponent.BaseTestingActivity"
+            android:label="LauncherTestApp"
+            android:taskAffinity="com.android.launcher3.testcomponent.Affinity1">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+            <intent-filter>
+                <action android:name="com.android.launcher3.intent.action.test_shortcut"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+            <meta-data android:name="android.app.shortcuts"
+                       android:resource="@xml/shortcuts"/>
+        </activity>
+        <activity-alias android:name="Activity2"
+                        android:label="TestActivity2"
+                        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>
+        <activity-alias android:name="Activity3"
+                        android:label="TestActivity3"
+                        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>
+        <activity-alias android:name="Activity4"
+                        android:label="TestActivity4"
+                        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>
+        <activity-alias android:name="Activity5"
+                        android:label="TestActivity5"
+                        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>
+        <activity-alias android:name="Activity6"
+                        android:label="TestActivity6"
+                        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>
+        <activity-alias android:name="Activity7"
+                        android:label="TestActivity7"
+                        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>
+        <activity-alias android:name="Activity8"
+                        android:label="TestActivity8"
+                        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>
+        <activity-alias android:name="Activity9"
+                        android:label="TestActivity9"
+                        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>
+        <activity-alias android:name="Activity10"
+                        android:label="TestActivity10"
+                        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>
+        <activity-alias android:name="Activity11"
+                        android:label="TestActivity11"
+                        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>
     </application>
 </manifest>
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index ec89f9c..24b5b02 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -18,7 +18,7 @@
     xmlns:tools="http://schemas.android.com/tools"
     package="com.android.launcher3.tests">
 
-    <uses-sdk android:targetSdkVersion="25" android:minSdkVersion="21"
+    <uses-sdk android:targetSdkVersion="28" android:minSdkVersion="25"
               tools:overrideLibrary="android.support.test.uiautomator.v18"/>
 
     <application android:debuggable="true">
@@ -28,7 +28,7 @@
     <instrumentation
         android:functionalTest="false"
         android:handleProfiling="false"
-        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:name="androidx.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.launcher3" >
     </instrumentation>
 </manifest>
diff --git a/tests/OWNERS b/tests/OWNERS
new file mode 100644
index 0000000..046d871
--- /dev/null
+++ b/tests/OWNERS
@@ -0,0 +1 @@
+vadimt@google.com
diff --git a/tests/dummy_app/Android.mk b/tests/dummy_app/Android.mk
new file mode 100644
index 0000000..f4ab582
--- /dev/null
+++ b/tests/dummy_app/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := samples
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := Aardwolf
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
+
+# Use the following include to make our test apk.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/dummy_app/AndroidManifest.xml b/tests/dummy_app/AndroidManifest.xml
new file mode 100644
index 0000000..9d0a74a
--- /dev/null
+++ b/tests/dummy_app/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?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.
+-->
+
+<!-- Declare the contents of this Android application.  The namespace
+     attribute brings in the Android platform namespace, and the package
+     supplies a unique name for the application.  When writing your
+     own application, the package name must be changed from "com.example.*"
+     to come from a domain that you own or have control over. -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.aardwolf">
+    <uses-sdk android:targetSdkVersion="28" android:minSdkVersion="21"/>
+    <application android:label="Aardwolf">
+        <activity
+            android:name="Activity1"
+            android:icon="@mipmap/ic_launcher1"
+            android:label="Aardwolf">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/dummy_app/res/layout/empty_activity.xml b/tests/dummy_app/res/layout/empty_activity.xml
new file mode 100644
index 0000000..377c56b
--- /dev/null
+++ b/tests/dummy_app/res/layout/empty_activity.xml
@@ -0,0 +1,24 @@
+<?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.
+-->
+
+<EditText xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/text"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:textSize="18sp"
+    android:autoText="true"
+    android:capitalize="sentences"
+    android:text="Did you enjoy the adaptive icon?" />
+
diff --git a/tests/dummy_app/res/mipmap-anydpi/ic_launcher1.xml b/tests/dummy_app/res/mipmap-anydpi/ic_launcher1.xml
new file mode 100644
index 0000000..37c8c73
--- /dev/null
+++ b/tests/dummy_app/res/mipmap-anydpi/ic_launcher1.xml
@@ -0,0 +1,21 @@
+<?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="@mipmap/icon_back_1"/>
+    <foreground>
+        <bitmap android:src="@mipmap/icon_fore_1"/>
+    </foreground>
+</adaptive-icon>
diff --git a/tests/dummy_app/res/mipmap-anydpi/ic_launcher2.xml b/tests/dummy_app/res/mipmap-anydpi/ic_launcher2.xml
new file mode 100644
index 0000000..20b2986
--- /dev/null
+++ b/tests/dummy_app/res/mipmap-anydpi/ic_launcher2.xml
@@ -0,0 +1,19 @@
+<?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/background" />
+    <foreground android:drawable="@mipmap/icon_fore_1"/>
+</adaptive-icon>
diff --git a/tests/dummy_app/res/mipmap-xxhdpi/ic_launcher1.png b/tests/dummy_app/res/mipmap-xxhdpi/ic_launcher1.png
new file mode 100644
index 0000000..73bc8e6
--- /dev/null
+++ b/tests/dummy_app/res/mipmap-xxhdpi/ic_launcher1.png
Binary files differ
diff --git a/tests/dummy_app/res/mipmap-xxhdpi/ic_launcher2.png b/tests/dummy_app/res/mipmap-xxhdpi/ic_launcher2.png
new file mode 100644
index 0000000..cd40b63
--- /dev/null
+++ b/tests/dummy_app/res/mipmap-xxhdpi/ic_launcher2.png
Binary files differ
diff --git a/tests/dummy_app/res/mipmap-xxhdpi/icon_back_1.png b/tests/dummy_app/res/mipmap-xxhdpi/icon_back_1.png
new file mode 100644
index 0000000..8debef3
--- /dev/null
+++ b/tests/dummy_app/res/mipmap-xxhdpi/icon_back_1.png
Binary files differ
diff --git a/tests/dummy_app/res/mipmap-xxhdpi/icon_fore_1.png b/tests/dummy_app/res/mipmap-xxhdpi/icon_fore_1.png
new file mode 100644
index 0000000..de4079b
--- /dev/null
+++ b/tests/dummy_app/res/mipmap-xxhdpi/icon_fore_1.png
Binary files differ
diff --git a/tests/dummy_app/res/mipmap-xxxhdpi/ic_launcher1.png b/tests/dummy_app/res/mipmap-xxxhdpi/ic_launcher1.png
new file mode 100644
index 0000000..889a99c
--- /dev/null
+++ b/tests/dummy_app/res/mipmap-xxxhdpi/ic_launcher1.png
Binary files differ
diff --git a/tests/dummy_app/res/mipmap-xxxhdpi/ic_launcher2.png b/tests/dummy_app/res/mipmap-xxxhdpi/ic_launcher2.png
new file mode 100644
index 0000000..973bb79
--- /dev/null
+++ b/tests/dummy_app/res/mipmap-xxxhdpi/ic_launcher2.png
Binary files differ
diff --git a/tests/dummy_app/res/mipmap-xxxhdpi/icon_back_1.png b/tests/dummy_app/res/mipmap-xxxhdpi/icon_back_1.png
new file mode 100644
index 0000000..70c0ebd
--- /dev/null
+++ b/tests/dummy_app/res/mipmap-xxxhdpi/icon_back_1.png
Binary files differ
diff --git a/tests/dummy_app/res/mipmap-xxxhdpi/icon_fore_1.png b/tests/dummy_app/res/mipmap-xxxhdpi/icon_fore_1.png
new file mode 100644
index 0000000..9d91632
--- /dev/null
+++ b/tests/dummy_app/res/mipmap-xxxhdpi/icon_fore_1.png
Binary files differ
diff --git a/tests/dummy_app/res/values/colors.xml b/tests/dummy_app/res/values/colors.xml
new file mode 100644
index 0000000..b5ce66e
--- /dev/null
+++ b/tests/dummy_app/res/values/colors.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+  <color name="background">#455A64</color>
+</resources>
\ No newline at end of file
diff --git a/tests/dummy_app/src/com/example/android/aardwolf/Activity1.java b/tests/dummy_app/src/com/example/android/aardwolf/Activity1.java
new file mode 100644
index 0000000..d4eab15
--- /dev/null
+++ b/tests/dummy_app/src/com/example/android/aardwolf/Activity1.java
@@ -0,0 +1,31 @@
+/*
+ * 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.example.android.aardwolf;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+
+public class Activity1 extends Activity {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        View view = getLayoutInflater().inflate(R.layout.empty_activity, null);
+        setContentView(view);
+    }
+}
+
diff --git a/tests/res/raw/aardwolf_dummy_app.apk b/tests/res/raw/aardwolf_dummy_app.apk
new file mode 100644
index 0000000..c10732d
--- /dev/null
+++ b/tests/res/raw/aardwolf_dummy_app.apk
Binary files differ
diff --git a/tests/res/raw/cache_data_updated_task_data.txt b/tests/res/raw/cache_data_updated_task_data.txt
deleted file mode 100644
index 8199687..0000000
--- a/tests/res/raw/cache_data_updated_task_data.txt
+++ /dev/null
@@ -1,28 +0,0 @@
-# Model data used by CacheDataUpdatedTaskTest
-
-classMap s com.android.launcher3.ShortcutInfo
-
-# 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
-
-# Auto install app shortcut
-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
-
-# Custom shortcuts
-bgItem s itemType=1 title=app1-shrt intent=component=app1/class3 id=7
-bgItem s itemType=1 title=app4-shrt intent=component=app4/class1 id=8
-
-# Restored custom shortcut
-bgItem s itemType=1 status=1 title=app3-shrt intent=component=app3/class3 id=9
-bgItem s itemType=1 status=1 title=app5-shrt intent=component=app5/class1 id=10
-
-allApps componentName=app1/class1 intent=component=app1/class1
-allApps componentName=app1/class2 intent=component=app1/class2
-allApps componentName=app2/class1 intent=component=app2/class1
-allApps componentName=app2/class2 intent=component=app2/class2
\ No newline at end of file
diff --git a/tests/res/raw/package_install_state_change_task_data.txt b/tests/res/raw/package_install_state_change_task_data.txt
deleted file mode 100644
index 84f9c16..0000000
--- a/tests/res/raw/package_install_state_change_task_data.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-# Model data used by PackageInstallStateChangeTaskTest
-
-classMap s com.android.launcher3.ShortcutInfo
-classMap w com.android.launcher3.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/tests/res/values/strings.xml b/tests/res/values/strings.xml
new file mode 100644
index 0000000..0ad87fb
--- /dev/null
+++ b/tests/res/values/strings.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="shortcut1" translatable="false">Shortcut 1</string>
+    <string name="shortcut2" translatable="false">Shortcut 2</string>
+    <string name="shortcut3" translatable="false">Shortcut 3</string>
+</resources>
diff --git a/tests/res/xml/shortcuts.xml b/tests/res/xml/shortcuts.xml
new file mode 100644
index 0000000..bdc22f9
--- /dev/null
+++ b/tests/res/xml/shortcuts.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
+    <shortcut
+        android:shortcutId="shortcut1"
+        android:shortcutShortLabel="@string/shortcut1">
+        <intent android:action="com.android.launcher3.intent.action.test_shortcut"/>
+    </shortcut>
+    <shortcut
+        android:shortcutId="shortcut2"
+        android:shortcutShortLabel="@string/shortcut2">
+        <intent android:action="com.android.launcher3.intent.action.test_shortcut"/>
+    </shortcut>
+    <shortcut
+        android:shortcutId="shortcut3"
+        android:shortcutShortLabel="@string/shortcut3">
+        <intent android:action="com.android.launcher3.intent.action.test_shortcut"/>
+    </shortcut>
+</shortcuts>
diff --git a/tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.java b/tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.java
index 846a163..293b04a 100644
--- a/tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.java
+++ b/tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.java
@@ -15,21 +15,23 @@
  */
 package com.android.launcher3.allapps.search;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 import android.content.ComponentName;
-import android.support.test.runner.AndroidJUnit4;
 
 import com.android.launcher3.AppInfo;
-import com.android.launcher3.Utilities;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 /**
  * Unit tests for {@link DefaultAppSearchAlgorithm}
  */
+@SmallTest
 @RunWith(AndroidJUnit4.class)
 public class DefaultAppSearchAlgorithmTest {
     private static final DefaultAppSearchAlgorithm.StringMatcher MATCHER =
@@ -75,9 +77,6 @@
 
     @Test
     public void testMatchesVN() {
-        if (!Utilities.ATLEAST_NOUGAT) {
-            return;
-        }
         assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("다운로드"), "다", MATCHER));
         assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("드라이브"), "드", MATCHER));
         assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("다운로드 드라이브"), "ㄷ", MATCHER));
diff --git a/tests/src/com/android/launcher3/logging/FileLogTest.java b/tests/src/com/android/launcher3/logging/FileLogTest.java
deleted file mode 100644
index 9c7cb8f..0000000
--- a/tests/src/com/android/launcher3/logging/FileLogTest.java
+++ /dev/null
@@ -1,94 +0,0 @@
-package com.android.launcher3.logging;
-
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.File;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.Calendar;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-/**
- * Tests for {@link FileLog}
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class FileLogTest {
-
-    private File mTempDir;
-
-    @Before
-    public void setUp() throws Exception {
-        int count = 0;
-        do {
-            mTempDir = new File(InstrumentationRegistry.getTargetContext().getCacheDir(),
-                    "log-test-" + (count++));
-        } while(!mTempDir.mkdir());
-
-        FileLog.setDir(mTempDir);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        // Clear existing logs
-        new File(mTempDir, "log-0").delete();
-        new File(mTempDir, "log-1").delete();
-        mTempDir.delete();
-    }
-
-    @Test
-    public void testPrintLog() throws Exception {
-        if (!FileLog.ENABLED) {
-            return;
-        }
-        FileLog.print("Testing", "hoolalala");
-        StringWriter writer = new StringWriter();
-        FileLog.flushAll(new PrintWriter(writer));
-        assertTrue(writer.toString().contains("hoolalala"));
-
-        FileLog.print("Testing", "abracadabra", new Exception("cat! cat!"));
-        writer = new StringWriter();
-        FileLog.flushAll(new PrintWriter(writer));
-        assertTrue(writer.toString().contains("abracadabra"));
-        // Exception is also printed
-        assertTrue(writer.toString().contains("cat! cat!"));
-
-        // Old logs still present after flush
-        assertTrue(writer.toString().contains("hoolalala"));
-    }
-
-    @Test
-    public void testOldFileTruncated() throws Exception {
-        if (!FileLog.ENABLED) {
-            return;
-        }
-        FileLog.print("Testing", "hoolalala");
-        StringWriter writer = new StringWriter();
-        FileLog.flushAll(new PrintWriter(writer));
-        assertTrue(writer.toString().contains("hoolalala"));
-
-        Calendar threeDaysAgo = Calendar.getInstance();
-        threeDaysAgo.add(Calendar.HOUR, -72);
-        new File(mTempDir, "log-0").setLastModified(threeDaysAgo.getTimeInMillis());
-        new File(mTempDir, "log-1").setLastModified(threeDaysAgo.getTimeInMillis());
-
-        FileLog.print("Testing", "abracadabra", new Exception("cat! cat!"));
-        writer = new StringWriter();
-        FileLog.flushAll(new PrintWriter(writer));
-        assertTrue(writer.toString().contains("abracadabra"));
-        // Exception is also printed
-        assertTrue(writer.toString().contains("cat! cat!"));
-
-        // Old logs have been truncated
-        assertFalse(writer.toString().contains("hoolalala"));
-    }
-}
diff --git a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
deleted file mode 100644
index 401711d..0000000
--- a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
+++ /dev/null
@@ -1,190 +0,0 @@
-package com.android.launcher3.model;
-
-import android.content.ComponentName;
-import android.content.ContentProviderOperation;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Rect;
-import android.net.Uri;
-import android.support.test.runner.AndroidJUnit4;
-import android.util.Pair;
-
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.LauncherProvider;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.util.GridOccupancy;
-import com.android.launcher3.util.LongArrayMap;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.verify;
-
-/**
- * Tests for {@link AddWorkspaceItemsTask}
- */
-@RunWith(AndroidJUnit4.class)
-public class AddWorkspaceItemsTaskTest extends BaseModelUpdateTaskTestCase {
-
-    private final ComponentName mComponent1 = new ComponentName("a", "b");
-    private final ComponentName mComponent2 = new ComponentName("b", "b");
-
-    private ArrayList<Long> existingScreens;
-    private ArrayList<Long> newScreens;
-    private LongArrayMap<GridOccupancy> screenOccupancy;
-
-    @Before
-    public void initData() throws Exception {
-        existingScreens = new ArrayList<>();
-        screenOccupancy = new LongArrayMap<>();
-        newScreens = new ArrayList<>();
-
-        idp.numColumns = 5;
-        idp.numRows = 5;
-    }
-
-    private AddWorkspaceItemsTask newTask(ItemInfo... items) {
-        List<Pair<ItemInfo, Object>> list = new ArrayList<>();
-        for (ItemInfo item : items) {
-            list.add(Pair.create(item, null));
-        }
-        return new AddWorkspaceItemsTask(list) {
-
-            @Override
-            protected void updateScreens(Context context, ArrayList<Long> workspaceScreens) { }
-        };
-    }
-
-    @Test
-    public void testFindSpaceForItem_prefers_second() {
-        // First screen has only one hole of size 1
-        int nextId = setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3));
-
-        // Second screen has 2 holes of sizes 3x2 and 2x3
-        setupWorkspaceWithHoles(nextId, 2, new Rect(2, 0, 5, 2), new Rect(0, 2, 2, 5));
-
-        Pair<Long, int[]> spaceFound = newTask()
-                .findSpaceForItem(appState, bgDataModel, existingScreens, newScreens, 1, 1);
-        assertEquals(2L, (long) spaceFound.first);
-        assertTrue(screenOccupancy.get(spaceFound.first)
-                .isRegionVacant(spaceFound.second[0], spaceFound.second[1], 1, 1));
-
-        // Find a larger space
-        spaceFound = newTask()
-                .findSpaceForItem(appState, bgDataModel, existingScreens, newScreens, 2, 3);
-        assertEquals(2L, (long) spaceFound.first);
-        assertTrue(screenOccupancy.get(spaceFound.first)
-                .isRegionVacant(spaceFound.second[0], spaceFound.second[1], 2, 3));
-    }
-
-    @Test
-    public void testFindSpaceForItem_adds_new_screen() throws Exception {
-        // First screen has 2 holes of sizes 3x2 and 2x3
-        setupWorkspaceWithHoles(1, 1, new Rect(2, 0, 5, 2), new Rect(0, 2, 2, 5));
-        commitScreensToDb();
-
-        ArrayList<Long> oldScreens = new ArrayList<>(existingScreens);
-        Pair<Long, int[]> spaceFound = newTask()
-                .findSpaceForItem(appState, bgDataModel, existingScreens, newScreens, 3, 3);
-        assertFalse(oldScreens.contains(spaceFound.first));
-        assertTrue(newScreens.contains(spaceFound.first));
-    }
-
-    @Test
-    public void testAddItem_existing_item_ignored() throws Exception {
-        ShortcutInfo info = new ShortcutInfo();
-        info.intent = new Intent().setComponent(mComponent1);
-
-        // Setup a screen with a hole
-        setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3));
-        commitScreensToDb();
-
-        // Nothing was added
-        assertTrue(executeTaskForTest(newTask(info)).isEmpty());
-    }
-
-    @Test
-    public void testAddItem_some_items_added() throws Exception {
-        ShortcutInfo info = new ShortcutInfo();
-        info.intent = new Intent().setComponent(mComponent1);
-
-        ShortcutInfo info2 = new ShortcutInfo();
-        info2.intent = new Intent().setComponent(mComponent2);
-
-        // Setup a screen with a hole
-        setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3));
-        commitScreensToDb();
-
-        executeTaskForTest(newTask(info, info2)).get(0).run();
-        ArgumentCaptor<ArrayList> notAnimated = ArgumentCaptor.forClass(ArrayList.class);
-        ArgumentCaptor<ArrayList> animated = ArgumentCaptor.forClass(ArrayList.class);
-
-        // only info2 should be added because info was already added to the workspace
-        // in setupWorkspaceWithHoles()
-        verify(callbacks).bindAppsAdded(any(ArrayList.class), notAnimated.capture(),
-                animated.capture());
-        assertTrue(notAnimated.getValue().isEmpty());
-
-        assertEquals(1, animated.getValue().size());
-        assertTrue(animated.getValue().contains(info2));
-    }
-
-    private int setupWorkspaceWithHoles(int startId, long screenId, Rect... holes) {
-        GridOccupancy occupancy = new GridOccupancy(idp.numColumns, idp.numRows);
-        occupancy.markCells(0, 0, idp.numColumns, idp.numRows, true);
-        for (Rect r : holes) {
-            occupancy.markCells(r, false);
-        }
-
-        existingScreens.add(screenId);
-        screenOccupancy.append(screenId, occupancy);
-
-        for (int x = 0; x < idp.numColumns; x++) {
-            for (int y = 0; y < idp.numRows; y++) {
-                if (!occupancy.cells[x][y]) {
-                    continue;
-                }
-
-                ShortcutInfo info = new ShortcutInfo();
-                info.intent = new Intent().setComponent(mComponent1);
-                info.id = startId++;
-                info.screenId = screenId;
-                info.cellX = x;
-                info.cellY = y;
-                info.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
-                bgDataModel.addItem(targetContext, info, false);
-            }
-        }
-        return startId;
-    }
-
-    private void commitScreensToDb() throws Exception {
-        LauncherSettings.Settings.call(mProviderRule.getResolver(),
-                LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
-
-        Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
-        ArrayList<ContentProviderOperation> ops = new ArrayList<>();
-        // Clear the table
-        ops.add(ContentProviderOperation.newDelete(uri).build());
-        int count = existingScreens.size();
-        for (int i = 0; i < count; i++) {
-            ContentValues v = new ContentValues();
-            long screenId = existingScreens.get(i);
-            v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
-            v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
-            ops.add(ContentProviderOperation.newInsert(uri).withValues(v).build());
-        }
-        mProviderRule.getResolver().applyBatch(LauncherProvider.AUTHORITY, ops);
-    }
-}
diff --git a/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java b/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
deleted file mode 100644
index b217847..0000000
--- a/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
+++ /dev/null
@@ -1,234 +0,0 @@
-package com.android.launcher3.model;
-
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.Intent;
-import android.content.pm.LauncherActivityInfo;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.Color;
-import android.os.Process;
-import android.os.UserHandle;
-import android.support.annotation.NonNull;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.rule.provider.ProviderTestRule;
-
-import com.android.launcher3.AllAppsList;
-import com.android.launcher3.AppFilter;
-import com.android.launcher3.AppInfo;
-import com.android.launcher3.IconCache;
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherModel;
-import com.android.launcher3.LauncherModel.Callbacks;
-import com.android.launcher3.LauncherModel.ModelUpdateTask;
-import com.android.launcher3.LauncherProvider;
-import com.android.launcher3.graphics.BitmapInfo;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.Provider;
-import com.android.launcher3.util.TestLauncherProvider;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.mockito.ArgumentCaptor;
-
-import java.io.BufferedReader;
-import java.io.InputStreamReader;
-import java.lang.reflect.Field;
-import java.util.HashMap;
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * Base class for writing tests for Model update tasks.
- */
-public class BaseModelUpdateTaskTestCase {
-
-    @Rule
-    public ProviderTestRule mProviderRule =
-            new ProviderTestRule.Builder(TestLauncherProvider.class, LauncherProvider.AUTHORITY)
-                    .build();
-
-    public final HashMap<Class, HashMap<String, Field>> fieldCache = new HashMap<>();
-
-    public Context targetContext;
-    public UserHandle myUser;
-
-    public InvariantDeviceProfile idp;
-    public LauncherAppState appState;
-    public LauncherModel model;
-    public ModelWriter modelWriter;
-    public MyIconCache iconCache;
-
-    public BgDataModel bgDataModel;
-    public AllAppsList allAppsList;
-    public Callbacks callbacks;
-
-    @Before
-    public void setUp() throws Exception {
-        callbacks = mock(Callbacks.class);
-        appState = mock(LauncherAppState.class);
-        model = mock(LauncherModel.class);
-        modelWriter = mock(ModelWriter.class);
-
-        when(appState.getModel()).thenReturn(model);
-        when(model.getWriter(anyBoolean(), anyBoolean())).thenReturn(modelWriter);
-        when(model.getCallback()).thenReturn(callbacks);
-
-        myUser = Process.myUserHandle();
-
-        bgDataModel = new BgDataModel();
-        targetContext = new ContextWrapper(InstrumentationRegistry.getTargetContext()) {
-            @Override
-            public ContentResolver getContentResolver() {
-                return mProviderRule.getResolver();
-            }
-        };
-        idp = new InvariantDeviceProfile();
-        iconCache = new MyIconCache(targetContext, idp);
-
-        allAppsList = new AllAppsList(iconCache, new AppFilter());
-
-        when(appState.getIconCache()).thenReturn(iconCache);
-        when(appState.getInvariantDeviceProfile()).thenReturn(idp);
-        when(appState.getContext()).thenReturn(targetContext);
-
-    }
-
-    /**
-     * Synchronously executes the task and returns all the UI callbacks posted.
-     */
-    public List<Runnable> executeTaskForTest(ModelUpdateTask task) throws Exception {
-        when(model.isModelLoaded()).thenReturn(true);
-
-        Executor mockExecutor = mock(Executor.class);
-
-        task.init(appState, model, bgDataModel, allAppsList, mockExecutor);
-        task.run();
-        ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
-        verify(mockExecutor, atLeast(0)).execute(captor.capture());
-
-        return captor.getAllValues();
-    }
-
-    /**
-     * Initializes mock data for the test.
-     */
-    public void initializeData(String resourceName) throws Exception {
-        Context myContext = InstrumentationRegistry.getContext();
-        Resources res = myContext.getResources();
-        int id = res.getIdentifier(resourceName, "raw", myContext.getPackageName());
-        try (BufferedReader reader =
-                     new BufferedReader(new InputStreamReader(res.openRawResource(id)))) {
-            String line;
-            HashMap<String, Class> classMap = new HashMap<>();
-            while((line = reader.readLine()) != null) {
-                line = line.trim();
-                if (line.startsWith("#") || line.isEmpty()) {
-                    continue;
-                }
-                String[] commands = line.split(" ");
-                switch (commands[0]) {
-                    case "classMap":
-                        classMap.put(commands[1], Class.forName(commands[2]));
-                        break;
-                    case "bgItem":
-                        bgDataModel.addItem(targetContext,
-                                (ItemInfo) initItem(classMap.get(commands[1]), commands, 2), false);
-                        break;
-                    case "allApps":
-                        allAppsList.add((AppInfo) initItem(AppInfo.class, commands, 1), null);
-                        break;
-                }
-            }
-        }
-    }
-
-    private Object initItem(Class clazz, String[] fieldDef, int startIndex) throws Exception {
-        HashMap<String, Field> cache = fieldCache.get(clazz);
-        if (cache == null) {
-            cache = new HashMap<>();
-            Class c = clazz;
-            while (c != null) {
-                for (Field f : c.getDeclaredFields()) {
-                    f.setAccessible(true);
-                    cache.put(f.getName(), f);
-                }
-                c = c.getSuperclass();
-            }
-            fieldCache.put(clazz, cache);
-        }
-
-        Object item = clazz.newInstance();
-        for (int i = startIndex; i < fieldDef.length; i++) {
-            String[] fieldData = fieldDef[i].split("=", 2);
-            Field f = cache.get(fieldData[0]);
-            Class type = f.getType();
-            if (type == int.class || type == long.class) {
-                f.set(item, Integer.parseInt(fieldData[1]));
-            } else if (type == CharSequence.class || type == String.class) {
-                f.set(item, fieldData[1]);
-            } else if (type == Intent.class) {
-                if (!fieldData[1].startsWith("#Intent")) {
-                    fieldData[1] = "#Intent;" + fieldData[1] + ";end";
-                }
-                f.set(item, Intent.parseUri(fieldData[1], 0));
-            } else if (type == ComponentName.class) {
-                f.set(item, ComponentName.unflattenFromString(fieldData[1]));
-            } else {
-                throw new Exception("Added parsing logic for "
-                        + f.getName() + " of type " + f.getType());
-            }
-        }
-        return item;
-    }
-
-    public static class MyIconCache extends IconCache {
-
-        private final HashMap<ComponentKey, CacheEntry> mCache = new HashMap<>();
-
-        public MyIconCache(Context context, InvariantDeviceProfile idp) {
-            super(context, idp);
-        }
-
-        @Override
-        protected CacheEntry cacheLocked(
-                @NonNull ComponentName componentName,
-                @NonNull Provider<LauncherActivityInfo> infoProvider,
-                UserHandle user, boolean usePackageIcon, boolean useLowResIcon) {
-            CacheEntry entry = mCache.get(new ComponentKey(componentName, user));
-            if (entry == null) {
-                entry = new CacheEntry();
-                getDefaultIcon(user).applyTo(entry);
-            }
-            return entry;
-        }
-
-        public void addCache(ComponentName key, String title) {
-            CacheEntry entry = new CacheEntry();
-            entry.icon = newIcon();
-            entry.color = Color.RED;
-            entry.title = title;
-            mCache.put(new ComponentKey(key, Process.myUserHandle()), entry);
-        }
-
-        public Bitmap newIcon() {
-            return Bitmap.createBitmap(1, 1, Config.ARGB_8888);
-        }
-
-        @Override
-        protected BitmapInfo makeDefaultIcon(UserHandle user) {
-            return BitmapInfo.fromBitmap(newIcon());
-        }
-    }
-}
diff --git a/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java b/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
deleted file mode 100644
index ac9d319..0000000
--- a/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
+++ /dev/null
@@ -1,96 +0,0 @@
-package com.android.launcher3.model;
-
-import android.support.test.runner.AndroidJUnit4;
-
-import com.android.launcher3.AppInfo;
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.ShortcutInfo;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.Arrays;
-import java.util.HashSet;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNotSame;
-import static org.junit.Assert.assertNull;
-
-/**
- * Tests for {@link CacheDataUpdatedTask}
- */
-@RunWith(AndroidJUnit4.class)
-public class CacheDataUpdatedTaskTest extends BaseModelUpdateTaskTestCase {
-
-    private static final String NEW_LABEL_PREFIX = "new-label-";
-
-    @Before
-    public void initData() throws Exception {
-        initializeData("cache_data_updated_task_data");
-        // Add dummy entries in the cache to simulate update
-        for (ItemInfo info : bgDataModel.itemsIdMap) {
-            iconCache.addCache(info.getTargetComponent(), NEW_LABEL_PREFIX + info.id);
-        }
-    }
-
-    private CacheDataUpdatedTask newTask(int op, String... pkg) {
-        return new CacheDataUpdatedTask(op, myUser, new HashSet<>(Arrays.asList(pkg)));
-    }
-
-    @Test
-    public void testCacheUpdate_update_apps() throws Exception {
-        // Clear all icons from apps list so that its easy to check what was updated
-        for (AppInfo info : allAppsList.data) {
-            info.iconBitmap = null;
-        }
-
-        executeTaskForTest(newTask(CacheDataUpdatedTask.OP_CACHE_UPDATE, "app1"));
-
-        // Verify that only the app icons of app1 (id 1 & 2) are updated. Custom shortcut (id 7)
-        // is not updated
-        verifyUpdate(1L, 2L);
-
-        // Verify that only app1 var updated in allAppsList
-        assertFalse(allAppsList.data.isEmpty());
-        for (AppInfo info : allAppsList.data) {
-            if (info.componentName.getPackageName().equals("app1")) {
-                assertNotNull(info.iconBitmap);
-            } else {
-                assertNull(info.iconBitmap);
-            }
-        }
-    }
-
-    @Test
-    public void testSessionUpdate_ignores_normal_apps() throws Exception {
-        executeTaskForTest(newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, "app1"));
-
-        // app1 has no restored shortcuts. Verify that nothing was updated.
-        verifyUpdate();
-    }
-
-    @Test
-    public void testSessionUpdate_updates_pending_apps() throws Exception {
-        executeTaskForTest(newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, "app3"));
-
-        // app3 has only restored apps (id 5, 6) and shortcuts (id 9). Verify that only apps were
-        // were updated
-        verifyUpdate(5L, 6L);
-    }
-
-    private void verifyUpdate(Long... idsUpdated) {
-        HashSet<Long> updates = new HashSet<>(Arrays.asList(idsUpdated));
-        for (ItemInfo info : bgDataModel.itemsIdMap) {
-            if (updates.contains(info.id)) {
-                assertEquals(NEW_LABEL_PREFIX + info.id, info.title);
-                assertNotNull(((ShortcutInfo) info).iconBitmap);
-            } else {
-                assertNotSame(NEW_LABEL_PREFIX + info.id, info.title);
-                assertNull(((ShortcutInfo) info).iconBitmap);
-            }
-        }
-    }
-}
diff --git a/tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java b/tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
deleted file mode 100644
index 1d9148b..0000000
--- a/tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
+++ /dev/null
@@ -1,194 +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;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertNotSame;
-import static junit.framework.Assert.assertTrue;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import com.android.launcher3.LauncherProvider;
-import com.android.launcher3.LauncherProvider.DatabaseHelper;
-import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.R;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.File;
-
-/**
- * Tests for {@link DbDowngradeHelper}
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class DbDowngradeHelperTest {
-
-    private static final String SCHEMA_FILE = "test_schema.json";
-    private static final String DB_FILE = "test.db";
-
-    private Context mContext;
-    private File mSchemaFile;
-    private File mDbFile;
-
-    @Before
-    public void setup() {
-        mContext = InstrumentationRegistry.getTargetContext();
-        mSchemaFile = mContext.getFileStreamPath(SCHEMA_FILE);
-        mDbFile = mContext.getDatabasePath(DB_FILE);
-    }
-
-    @Test
-    public void testUpdateSchemaFile() throws Exception {
-        Context myContext = InstrumentationRegistry.getContext();
-        int testResId = myContext.getResources().getIdentifier(
-                "db_schema_v10", "raw", myContext.getPackageName());
-        mSchemaFile.delete();
-        assertFalse(mSchemaFile.exists());
-
-        DbDowngradeHelper.updateSchemaFile(mSchemaFile, 10, myContext, testResId);
-        assertTrue(mSchemaFile.exists());
-        assertEquals(10, DbDowngradeHelper.parse(mSchemaFile).version);
-
-        // Schema is updated on version upgrade
-        assertTrue(mSchemaFile.setLastModified(0));
-        DbDowngradeHelper.updateSchemaFile(mSchemaFile, 11, myContext, testResId);
-        assertNotSame(0, mSchemaFile.lastModified());
-
-        // Schema is not updated when version is same
-        assertTrue(mSchemaFile.setLastModified(0));
-        DbDowngradeHelper.updateSchemaFile(mSchemaFile, 10, myContext, testResId);
-        assertEquals(0, mSchemaFile.lastModified());
-
-        // Schema is not updated on version downgrade
-        DbDowngradeHelper.updateSchemaFile(mSchemaFile, 3, myContext, testResId);
-        assertEquals(0, mSchemaFile.lastModified());
-    }
-
-    @Test
-    public void testDowngrade_success_v24() throws Exception {
-        setupTestDb();
-
-        TestOpenHelper helper = new TestOpenHelper(24);
-        assertEquals(24, helper.getReadableDatabase().getVersion());
-        helper.close();
-    }
-
-    @Test
-    public void testDowngrade_success_v22() throws Exception {
-        setupTestDb();
-
-        SQLiteOpenHelper helper = new TestOpenHelper(22);
-        assertEquals(22, helper.getWritableDatabase().getVersion());
-
-        // Check column does not exist
-        try (Cursor c = helper.getWritableDatabase().query(Favorites.TABLE_NAME,
-                null, null, null, null, null, null)) {
-            assertEquals(-1, c.getColumnIndex(Favorites.OPTIONS));
-
-            // Check data is present
-            assertEquals(10, c.getCount());
-        }
-        helper.close();
-
-        helper = new DatabaseHelper(mContext, null, DB_FILE) {
-            @Override
-            public void onOpen(SQLiteDatabase db) { }
-        };
-        assertEquals(LauncherProvider.SCHEMA_VERSION, helper.getWritableDatabase().getVersion());
-
-        try (Cursor c = helper.getWritableDatabase().query(Favorites.TABLE_NAME,
-                null, null, null, null, null, null)) {
-            // Check column exists
-            assertNotSame(-1, c.getColumnIndex(Favorites.OPTIONS));
-
-            // Check data is present
-            assertEquals(10, c.getCount());
-        }
-        helper.close();
-    }
-
-    @Test(expected = DowngradeFailException.class)
-    public void testDowngrade_fail_v20() throws Exception {
-        setupTestDb();
-
-        TestOpenHelper helper = new TestOpenHelper(20);
-        helper.getReadableDatabase().getVersion();
-    }
-
-    private void setupTestDb() throws Exception {
-        mSchemaFile.delete();
-        mDbFile.delete();
-
-        DbDowngradeHelper.updateSchemaFile(mSchemaFile, LauncherProvider.SCHEMA_VERSION, mContext,
-                R.raw.downgrade_schema);
-
-        DatabaseHelper dbHelper = new DatabaseHelper(mContext, null, DB_FILE) {
-            @Override
-            public void onOpen(SQLiteDatabase db) { }
-        };
-        // Insert dummy data
-        for (int i = 0; i < 10; i++) {
-            ContentValues values = new ContentValues();
-            values.put(Favorites._ID, i);
-            values.put(Favorites.TITLE, "title " + i);
-            dbHelper.getWritableDatabase().insert(Favorites.TABLE_NAME, null, values);
-        }
-        dbHelper.close();
-    }
-
-    private class TestOpenHelper extends SQLiteOpenHelper {
-
-        public TestOpenHelper(int version) {
-            super(mContext, DB_FILE, null, version);
-        }
-
-        @Override
-        public void onCreate(SQLiteDatabase sqLiteDatabase) {
-            throw new RuntimeException("DB should already be created");
-        }
-
-        @Override
-        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
-            throw new RuntimeException("Only downgrade supported");
-        }
-
-        @Override
-        public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
-            try {
-                DbDowngradeHelper.parse(mSchemaFile).onDowngrade(db, oldVersion, newVersion);
-            } catch (Exception e) {
-                throw new DowngradeFailException(e);
-            }
-        }
-    }
-
-    private static class DowngradeFailException extends RuntimeException {
-        public DowngradeFailException(Exception e) {
-            super(e);
-        }
-    }
-}
diff --git a/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java b/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
deleted file mode 100644
index b92f612..0000000
--- a/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
+++ /dev/null
@@ -1,477 +0,0 @@
-package com.android.launcher3.model;
-
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.Intent;
-import android.database.Cursor;
-import android.graphics.Point;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.MediumTest;
-import android.support.test.rule.provider.ProviderTestRule;
-import android.support.test.runner.AndroidJUnit4;
-
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.LauncherModel;
-import com.android.launcher3.LauncherProvider;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.model.GridSizeMigrationTask.MultiStepMigrationTask;
-import com.android.launcher3.util.TestLauncherProvider;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.LinkedList;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-/**
- * Unit tests for {@link GridSizeMigrationTask}
- */
-@MediumTest
-@RunWith(AndroidJUnit4.class)
-public class GridSizeMigrationTaskTest {
-
-    @Rule
-    public ProviderTestRule mProviderRule =
-            new ProviderTestRule.Builder(TestLauncherProvider.class, LauncherProvider.AUTHORITY)
-                    .build();
-
-    private static final long DESKTOP = LauncherSettings.Favorites.CONTAINER_DESKTOP;
-    private static final long HOTSEAT = LauncherSettings.Favorites.CONTAINER_HOTSEAT;
-
-    private static final int APPLICATION = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
-    private static final int SHORTCUT = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
-
-    private static final String TEST_PACKAGE = "com.android.launcher3.validpackage";
-    private static final String VALID_INTENT =
-            new Intent(Intent.ACTION_MAIN).setPackage(TEST_PACKAGE).toUri(0);
-
-    private HashSet<String> mValidPackages;
-    private InvariantDeviceProfile mIdp;
-    private Context mContext;
-
-    @Before
-    public void setUp() throws Exception {
-        mValidPackages = new HashSet<>();
-        mValidPackages.add(TEST_PACKAGE);
-
-        mIdp = new InvariantDeviceProfile();
-
-        mContext = new ContextWrapper(InstrumentationRegistry.getTargetContext()) {
-
-            @Override
-            public ContentResolver getContentResolver() {
-                return mProviderRule.getResolver();
-            }
-        };
-    }
-
-    @Test
-    public void testHotseatMigration_apps_dropped() throws Exception {
-        long[] hotseatItems = {
-                addItem(APPLICATION, 0, HOTSEAT, 0, 0),
-                addItem(SHORTCUT, 1, HOTSEAT, 0, 0),
-                -1,
-                addItem(SHORTCUT, 3, HOTSEAT, 0, 0),
-                addItem(APPLICATION, 4, HOTSEAT, 0, 0),
-        };
-
-        mIdp.numHotseatIcons = 3;
-        new GridSizeMigrationTask(mContext, mIdp, mValidPackages, 5, 3)
-                .migrateHotseat();
-        if (FeatureFlags.NO_ALL_APPS_ICON) {
-            // First item is dropped as it has the least weight.
-            verifyHotseat(hotseatItems[1], hotseatItems[3], hotseatItems[4]);
-        } else {
-            // First & last items are dropped as they have the least weight.
-            verifyHotseat(hotseatItems[1], -1, hotseatItems[3]);
-        }
-    }
-
-    @Test
-    public void testHotseatMigration_shortcuts_dropped() throws Exception {
-        long[] hotseatItems = {
-                addItem(APPLICATION, 0, HOTSEAT, 0, 0),
-                addItem(30, 1, HOTSEAT, 0, 0),
-                -1,
-                addItem(SHORTCUT, 3, HOTSEAT, 0, 0),
-                addItem(10, 4, HOTSEAT, 0, 0),
-        };
-
-        mIdp.numHotseatIcons = 3;
-        new GridSizeMigrationTask(mContext, mIdp, mValidPackages, 5, 3)
-                .migrateHotseat();
-        if (FeatureFlags.NO_ALL_APPS_ICON) {
-            // First item is dropped as it has the least weight.
-            verifyHotseat(hotseatItems[1], hotseatItems[3], hotseatItems[4]);
-        } else {
-            // First & third items are dropped as they have the least weight.
-            verifyHotseat(hotseatItems[1], -1, hotseatItems[4]);
-        }
-    }
-
-    private void verifyHotseat(long... sortedIds) {
-        int screenId = 0;
-        int total = 0;
-
-        for (long id : sortedIds) {
-            Cursor c = mProviderRule.getResolver().query(LauncherSettings.Favorites.CONTENT_URI,
-                    new String[]{LauncherSettings.Favorites._ID},
-                    "container=-101 and screen=" + screenId, null, null, null);
-
-            if (id == -1) {
-                assertEquals(0, c.getCount());
-            } else {
-                assertEquals(1, c.getCount());
-                c.moveToNext();
-                assertEquals(id, c.getLong(0));
-                total ++;
-            }
-            c.close();
-
-            screenId++;
-        }
-
-        // Verify that not other entry exist in the DB.
-        Cursor c = mProviderRule.getResolver().query(LauncherSettings.Favorites.CONTENT_URI,
-                new String[]{LauncherSettings.Favorites._ID},
-                "container=-101", null, null, null);
-        assertEquals(total, c.getCount());
-        c.close();
-    }
-
-    @Test
-    public void testWorkspace_empty_row_column_removed() throws Exception {
-        long[][][] ids = createGrid(new int[][][]{{
-                {  0,  0, -1,  1},
-                {  3,  1, -1,  4},
-                { -1, -1, -1, -1},
-                {  5,  2, -1,  6},
-        }});
-
-        new GridSizeMigrationTask(mContext, mIdp, mValidPackages,
-                new Point(4, 4), new Point(3, 3)).migrateWorkspace();
-
-        // Column 2 and row 2 got removed.
-        verifyWorkspace(new long[][][] {{
-                {ids[0][0][0], ids[0][0][1], ids[0][0][3]},
-                {ids[0][1][0], ids[0][1][1], ids[0][1][3]},
-                {ids[0][3][0], ids[0][3][1], ids[0][3][3]},
-        }});
-    }
-
-    @Test
-    public void testWorkspace_new_screen_created() throws Exception {
-        long[][][] ids = createGrid(new int[][][]{{
-                {  0,  0,  0,  1},
-                {  3,  1,  0,  4},
-                { -1, -1, -1, -1},
-                {  5,  2, -1,  6},
-        }});
-
-        new GridSizeMigrationTask(mContext, mIdp, mValidPackages,
-                new Point(4, 4), new Point(3, 3)).migrateWorkspace();
-
-        // Items in the second column get moved to new screen
-        verifyWorkspace(new long[][][] {{
-                {ids[0][0][0], ids[0][0][1], ids[0][0][3]},
-                {ids[0][1][0], ids[0][1][1], ids[0][1][3]},
-                {ids[0][3][0], ids[0][3][1], ids[0][3][3]},
-        }, {
-                {ids[0][0][2], ids[0][1][2], -1},
-        }});
-    }
-
-    @Test
-    public void testWorkspace_items_merged_in_next_screen() throws Exception {
-        long[][][] ids = createGrid(new int[][][]{{
-                {  0,  0,  0,  1},
-                {  3,  1,  0,  4},
-                { -1, -1, -1, -1},
-                {  5,  2, -1,  6},
-        },{
-                {  0,  0, -1,  1},
-                {  3,  1, -1,  4},
-        }});
-
-        new GridSizeMigrationTask(mContext, mIdp, mValidPackages,
-                new Point(4, 4), new Point(3, 3)).migrateWorkspace();
-
-        // Items in the second column of the first screen should get placed on the 3rd
-        // row of the second screen
-        verifyWorkspace(new long[][][] {{
-                {ids[0][0][0], ids[0][0][1], ids[0][0][3]},
-                {ids[0][1][0], ids[0][1][1], ids[0][1][3]},
-                {ids[0][3][0], ids[0][3][1], ids[0][3][3]},
-        }, {
-                {ids[1][0][0], ids[1][0][1], ids[1][0][3]},
-                {ids[1][1][0], ids[1][1][1], ids[1][1][3]},
-                {ids[0][0][2], ids[0][1][2], -1},
-        }});
-    }
-
-    @Test
-    public void testWorkspace_items_not_merged_in_next_screen() throws Exception {
-        // First screen has 2 items that need to be moved, but second screen has only one
-        // empty space after migration (top-left corner)
-        long[][][] ids = createGrid(new int[][][]{{
-                {  0,  0,  0,  1},
-                {  3,  1,  0,  4},
-                { -1, -1, -1, -1},
-                {  5,  2, -1,  6},
-        },{
-                { -1,  0, -1,  1},
-                {  3,  1, -1,  4},
-                { -1, -1, -1, -1},
-                {  5,  2, -1,  6},
-        }});
-
-        new GridSizeMigrationTask(mContext, mIdp, mValidPackages,
-                new Point(4, 4), new Point(3, 3)).migrateWorkspace();
-
-        // Items in the second column of the first screen should get placed on a new screen.
-        verifyWorkspace(new long[][][] {{
-                {ids[0][0][0], ids[0][0][1], ids[0][0][3]},
-                {ids[0][1][0], ids[0][1][1], ids[0][1][3]},
-                {ids[0][3][0], ids[0][3][1], ids[0][3][3]},
-        }, {
-                {          -1, ids[1][0][1], ids[1][0][3]},
-                {ids[1][1][0], ids[1][1][1], ids[1][1][3]},
-                {ids[1][3][0], ids[1][3][1], ids[1][3][3]},
-        }, {
-                {ids[0][0][2], ids[0][1][2], -1},
-        }});
-    }
-
-    @Test
-    public void testWorkspace_first_row_blocked() throws Exception {
-        // The first screen has one item on the 4th column which needs moving, as the first row
-        // will be kept empty.
-        long[][][] ids = createGrid(new int[][][]{{
-                { -1, -1, -1, -1},
-                {  3,  1,  7,  0},
-                {  8,  7,  7, -1},
-                {  5,  2,  7, -1},
-        }}, 0);
-
-        new GridSizeMigrationTask(mContext, mIdp, mValidPackages,
-                new Point(4, 4), new Point(3, 4)).migrateWorkspace();
-
-        // Items in the second column of the first screen should get placed on a new screen.
-        verifyWorkspace(new long[][][] {{
-                {          -1,           -1,           -1},
-                {ids[0][1][0], ids[0][1][1], ids[0][1][2]},
-                {ids[0][2][0], ids[0][2][1], ids[0][2][2]},
-                {ids[0][3][0], ids[0][3][1], ids[0][3][2]},
-        }, {
-                {ids[0][1][3]},
-        }});
-    }
-
-    @Test
-    public void testWorkspace_items_moved_to_empty_first_row() throws Exception {
-        // Items will get moved to the next screen to keep the first screen empty.
-        long[][][] ids = createGrid(new int[][][]{{
-                { -1, -1, -1, -1},
-                {  0,  1,  0,  0},
-                {  8,  7,  7, -1},
-                {  5,  6,  7, -1},
-        }}, 0);
-
-        new GridSizeMigrationTask(mContext, mIdp, mValidPackages,
-                new Point(4, 4), new Point(3, 3)).migrateWorkspace();
-
-        // Items in the second column of the first screen should get placed on a new screen.
-        verifyWorkspace(new long[][][] {{
-                {          -1,           -1,           -1},
-                {ids[0][2][0], ids[0][2][1], ids[0][2][2]},
-                {ids[0][3][0], ids[0][3][1], ids[0][3][2]},
-        }, {
-                {ids[0][1][1], ids[0][1][0], ids[0][1][2]},
-                {ids[0][1][3]},
-        }});
-    }
-
-    private long[][][] createGrid(int[][][] typeArray) throws Exception {
-        return createGrid(typeArray, 1);
-    }
-
-    /**
-     * Initializes the DB with dummy 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.
-     * @return the same grid representation where each entry is the corresponding item id.
-     */
-    private long[][][] createGrid(int[][][] typeArray, long startScreen) throws Exception {
-        LauncherSettings.Settings.call(mProviderRule.getResolver(),
-                LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
-        long[][][] ids = new long[typeArray.length][][];
-
-        for (int i = 0; i < typeArray.length; i++) {
-            // Add screen to DB
-            long screenId = startScreen + i;
-
-            // Keep the screen id counter up to date
-            LauncherSettings.Settings.call(mProviderRule.getResolver(),
-                    LauncherSettings.Settings.METHOD_NEW_SCREEN_ID);
-
-            ContentValues v = new ContentValues();
-            v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
-            v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
-            mProviderRule.getResolver().insert(LauncherSettings.WorkspaceScreens.CONTENT_URI, v);
-
-            ids[i] = new long[typeArray[i].length][];
-            for (int y = 0; y < typeArray[i].length; y++) {
-                ids[i][y] = new long[typeArray[i][y].length];
-                for (int x = 0; x < typeArray[i][y].length; x++) {
-                    if (typeArray[i][y][x] < 0) {
-                        // Empty cell
-                        ids[i][y][x] = -1;
-                    } else {
-                        ids[i][y][x] = addItem(typeArray[i][y][x], screenId, DESKTOP, x, y);
-                    }
-                }
-            }
-        }
-        return ids;
-    }
-
-    /**
-     * Verifies that the workspace items are arranged in the provided order.
-     * @param ids A 3d array where the first dimension represents the screen, and the rest two
-     *            represent the workspace grid.
-     */
-    private void verifyWorkspace(long[][][] ids) {
-        ArrayList<Long> allScreens = LauncherModel.loadWorkspaceScreensDb(mContext);
-        assertEquals(ids.length, allScreens.size());
-        int total = 0;
-
-        for (int i = 0; i < ids.length; i++) {
-            long screenId = allScreens.get(i);
-            for (int y = 0; y < ids[i].length; y++) {
-                for (int x = 0; x < ids[i][y].length; x++) {
-                    long id = ids[i][y][x];
-
-                    Cursor c = mProviderRule.getResolver().query(
-                            LauncherSettings.Favorites.CONTENT_URI,
-                            new String[]{LauncherSettings.Favorites._ID},
-                            "container=-100 and screen=" + screenId +
-                                    " and cellX=" + x + " and cellY=" + y, null, null, null);
-                    if (id == -1) {
-                        assertEquals(0, c.getCount());
-                    } else {
-                        assertEquals(1, c.getCount());
-                        c.moveToNext();
-                        assertEquals(String.format("Failed to verify item at %d %d, %d", i, y, x),
-                                id, c.getLong(0));
-                        total++;
-                    }
-                    c.close();
-                }
-            }
-        }
-
-        // Verify that not other entry exist in the DB.
-        Cursor c = mProviderRule.getResolver().query(LauncherSettings.Favorites.CONTENT_URI,
-                new String[]{LauncherSettings.Favorites._ID},
-                "container=-100", null, null, null);
-        assertEquals(total, c.getCount());
-        c.close();
-    }
-
-    /**
-     * Adds a dummy item in the DB.
-     * @param type {@link #APPLICATION} or {@link #SHORTCUT} or >= 2 for
-     *             folder (where the type represents the number of items in the folder).
-     */
-    private long addItem(int type, long screen, long container, int x, int y) throws Exception {
-        long id = LauncherSettings.Settings.call(mProviderRule.getResolver(),
-                LauncherSettings.Settings.METHOD_NEW_ITEM_ID)
-                .getLong(LauncherSettings.Settings.EXTRA_VALUE);
-
-        ContentValues values = new ContentValues();
-        values.put(LauncherSettings.Favorites._ID, id);
-        values.put(LauncherSettings.Favorites.CONTAINER, container);
-        values.put(LauncherSettings.Favorites.SCREEN, screen);
-        values.put(LauncherSettings.Favorites.CELLX, x);
-        values.put(LauncherSettings.Favorites.CELLY, y);
-        values.put(LauncherSettings.Favorites.SPANX, 1);
-        values.put(LauncherSettings.Favorites.SPANY, 1);
-
-        if (type == APPLICATION || type == SHORTCUT) {
-            values.put(LauncherSettings.Favorites.ITEM_TYPE, type);
-            values.put(LauncherSettings.Favorites.INTENT, VALID_INTENT);
-        } else {
-            values.put(LauncherSettings.Favorites.ITEM_TYPE,
-                    LauncherSettings.Favorites.ITEM_TYPE_FOLDER);
-            // Add folder items.
-            for (int i = 0; i < type; i++) {
-                addItem(APPLICATION, 0, id, 0, 0);
-            }
-        }
-
-        mProviderRule.getResolver().insert(LauncherSettings.Favorites.CONTENT_URI, values);
-        return id;
-    }
-
-    @Test
-    public void testMultiStepMigration_small_to_large() throws Exception {
-        MultiStepMigrationTaskVerifier verifier = new MultiStepMigrationTaskVerifier();
-        verifier.migrate(new Point(3, 3), new Point(5, 5));
-        verifier.assertCompleted();
-    }
-
-    @Test
-    public void testMultiStepMigration_large_to_small() throws Exception {
-        MultiStepMigrationTaskVerifier verifier = new MultiStepMigrationTaskVerifier(
-                5, 5, 4, 4,
-                4, 4, 3, 4
-        );
-        verifier.migrate(new Point(5, 5), new Point(3, 4));
-        verifier.assertCompleted();
-    }
-
-    @Test
-    public void testMultiStepMigration_zig_zag() throws Exception {
-        MultiStepMigrationTaskVerifier verifier = new MultiStepMigrationTaskVerifier(
-                5, 7, 4, 7,
-                4, 7, 3, 7
-        );
-        verifier.migrate(new Point(5, 5), new Point(3, 7));
-        verifier.assertCompleted();
-    }
-
-    private static class MultiStepMigrationTaskVerifier extends MultiStepMigrationTask {
-
-        private final LinkedList<Point> mPoints;
-
-        public MultiStepMigrationTaskVerifier(int... points) {
-            super(null, null);
-
-            mPoints = new LinkedList<>();
-            for (int i = 0; i < points.length; i += 2) {
-                mPoints.add(new Point(points[i], points[i + 1]));
-            }
-        }
-
-        @Override
-        protected boolean runStepTask(Point sourceSize, Point nextSize) throws Exception {
-            assertEquals(sourceSize, mPoints.poll());
-            assertEquals(nextSize, mPoints.poll());
-            return false;
-        }
-
-        public void assertCompleted() {
-            assertTrue(mPoints.isEmpty());
-        }
-    }
-}
diff --git a/tests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
index dfefa31..7d60ad6 100644
--- a/tests/src/com/android/launcher3/model/LoaderCursorTest.java
+++ b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -6,27 +6,24 @@
 import android.database.MatrixCursor;
 import android.graphics.Bitmap;
 import android.os.Process;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
-import com.android.launcher3.IconCache;
+import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.LauncherAppsCompat;
-import com.android.launcher3.graphics.BitmapInfo;
+import com.android.launcher3.icons.BitmapInfo;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-
-import static com.android.launcher3.LauncherSettings.BaseLauncherColumns.INTENT;
+import static com.android.launcher3.LauncherSettings.Favorites.INTENT;
 import static com.android.launcher3.LauncherSettings.Favorites.CELLX;
 import static com.android.launcher3.LauncherSettings.Favorites.CELLY;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER;
@@ -115,7 +112,7 @@
 
         ComponentName cn = mLauncherApps.getActivityList(null, mLoaderCursor.user)
                 .get(0).getComponentName();
-        ShortcutInfo info = mLoaderCursor.getAppShortcutInfo(
+        WorkspaceItemInfo info = mLoaderCursor.getAppShortcutInfo(
                 new Intent().setComponent(cn), false /* allowMissingTarget */, true);
         assertNotNull(info);
         assertTrue(Utilities.isLauncherAppTarget(info.intent));
@@ -127,7 +124,7 @@
         assertTrue(mLoaderCursor.moveToNext());
 
         ComponentName cn = new ComponentName(mContext.getPackageName(), "dummy-do");
-        ShortcutInfo info = mLoaderCursor.getAppShortcutInfo(
+        WorkspaceItemInfo info = mLoaderCursor.getAppShortcutInfo(
                 new Intent().setComponent(cn), true  /* allowMissingTarget */, true);
         assertNotNull(info);
         assertTrue(Utilities.isLauncherAppTarget(info.intent));
@@ -141,91 +138,67 @@
         Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
         when(mMockIconCache.getDefaultIcon(eq(mLoaderCursor.user)))
                 .thenReturn(BitmapInfo.fromBitmap(icon));
-        ShortcutInfo info = mLoaderCursor.loadSimpleShortcut();
+        WorkspaceItemInfo info = mLoaderCursor.loadSimpleWorkspaceItem();
         assertEquals(icon, info.iconBitmap);
         assertEquals("my-shortcut", info.title);
         assertEquals(ITEM_TYPE_SHORTCUT, info.itemType);
     }
 
     @Test
-    public void checkItemPlacement_wrongWorkspaceScreen() {
-        ArrayList<Long> workspaceScreens = new ArrayList<>(Arrays.asList(1L, 3L));
-        mIDP.numRows = 4;
-        mIDP.numColumns = 4;
-        mIDP.numHotseatIcons = 3;
-
-        // Item on unknown screen are not placed
-        assertFalse(mLoaderCursor.checkItemPlacement(
-                newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 4L), workspaceScreens));
-        assertFalse(mLoaderCursor.checkItemPlacement(
-                newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 5L), workspaceScreens));
-        assertFalse(mLoaderCursor.checkItemPlacement(
-                newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 2L), workspaceScreens));
-
-        assertTrue(mLoaderCursor.checkItemPlacement(
-                newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 1L), workspaceScreens));
-        assertTrue(mLoaderCursor.checkItemPlacement(
-                newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 3L), workspaceScreens));
-
-    }
-    @Test
     public void checkItemPlacement_outsideBounds() {
-        ArrayList<Long> workspaceScreens = new ArrayList<>(Arrays.asList(1L, 2L));
         mIDP.numRows = 4;
         mIDP.numColumns = 4;
         mIDP.numHotseatIcons = 3;
 
         // Item outside screen bounds are not placed
         assertFalse(mLoaderCursor.checkItemPlacement(
-                newItemInfo(4, 4, 1, 1, CONTAINER_DESKTOP, 1L), workspaceScreens));
+                newItemInfo(4, 4, 1, 1, CONTAINER_DESKTOP, 1)));
     }
 
     @Test
     public void checkItemPlacement_overlappingItems() {
-        ArrayList<Long> workspaceScreens = new ArrayList<>(Arrays.asList(1L, 2L));
         mIDP.numRows = 4;
         mIDP.numColumns = 4;
         mIDP.numHotseatIcons = 3;
 
         // Overlapping items are not placed
         assertTrue(mLoaderCursor.checkItemPlacement(
-                newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 1L), workspaceScreens));
+                newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 1)));
         assertFalse(mLoaderCursor.checkItemPlacement(
-                newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 1L), workspaceScreens));
+                newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 1)));
 
         assertTrue(mLoaderCursor.checkItemPlacement(
-                newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 2L), workspaceScreens));
+                newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 2)));
         assertFalse(mLoaderCursor.checkItemPlacement(
-                newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 2L), workspaceScreens));
+                newItemInfo(0, 0, 1, 1, CONTAINER_DESKTOP, 2)));
 
         assertTrue(mLoaderCursor.checkItemPlacement(
-                newItemInfo(1, 1, 1, 1, CONTAINER_DESKTOP, 1L), workspaceScreens));
+                newItemInfo(1, 1, 1, 1, CONTAINER_DESKTOP, 1)));
         assertTrue(mLoaderCursor.checkItemPlacement(
-                newItemInfo(2, 2, 2, 2, CONTAINER_DESKTOP, 1L), workspaceScreens));
+                newItemInfo(2, 2, 2, 2, CONTAINER_DESKTOP, 1)));
 
         assertFalse(mLoaderCursor.checkItemPlacement(
-                newItemInfo(3, 2, 1, 2, CONTAINER_DESKTOP, 1L), workspaceScreens));
+                newItemInfo(3, 2, 1, 2, CONTAINER_DESKTOP, 1)));
     }
 
     @Test
     public void checkItemPlacement_hotseat() {
-        ArrayList<Long> workspaceScreens = new ArrayList<>();
         mIDP.numRows = 4;
         mIDP.numColumns = 4;
         mIDP.numHotseatIcons = 3;
 
         // Hotseat items are only placed based on screenId
         assertTrue(mLoaderCursor.checkItemPlacement(
-                newItemInfo(3, 3, 1, 1, CONTAINER_HOTSEAT, 1L), workspaceScreens));
+                newItemInfo(3, 3, 1, 1, CONTAINER_HOTSEAT, 1)));
         assertTrue(mLoaderCursor.checkItemPlacement(
-                newItemInfo(3, 3, 1, 1, CONTAINER_HOTSEAT, 2L), workspaceScreens));
+                newItemInfo(3, 3, 1, 1, CONTAINER_HOTSEAT, 2)));
 
         assertFalse(mLoaderCursor.checkItemPlacement(
-                newItemInfo(3, 3, 1, 1, CONTAINER_HOTSEAT, 3L), workspaceScreens));
+                newItemInfo(3, 3, 1, 1, CONTAINER_HOTSEAT, 3)));
     }
 
     private ItemInfo newItemInfo(int cellX, int cellY, int spanX, int spanY,
-            long container, long screenId) {
+            int container, int screenId) {
         ItemInfo info = new ItemInfo();
         info.cellX = cellX;
         info.cellY = cellY;
diff --git a/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java b/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
deleted file mode 100644
index 0a741c4..0000000
--- a/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package com.android.launcher3.model;
-
-import android.support.test.runner.AndroidJUnit4;
-
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.LauncherAppWidgetInfo;
-import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.compat.PackageInstallerCompat;
-import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.Arrays;
-import java.util.HashSet;
-
-import static org.junit.Assert.assertEquals;
-
-/**
- * Tests for {@link PackageInstallStateChangedTask}
- */
-@RunWith(AndroidJUnit4.class)
-public class PackageInstallStateChangedTaskTest extends BaseModelUpdateTaskTestCase {
-
-    @Before
-    public void initData() throws Exception {
-        initializeData("package_install_state_change_task_data");
-    }
-
-    private PackageInstallStateChangedTask newTask(String pkg, int progress) {
-        int state = PackageInstallerCompat.STATUS_INSTALLING;
-        PackageInstallInfo installInfo = new PackageInstallInfo(pkg, state, progress);
-        return new PackageInstallStateChangedTask(installInfo);
-    }
-
-    @Test
-    public void testSessionUpdate_ignore_installed() throws Exception {
-        executeTaskForTest(newTask("app1", 30));
-
-        // No shortcuts were updated
-        verifyProgressUpdate(0);
-    }
-
-    @Test
-    public void testSessionUpdate_shortcuts_updated() throws Exception {
-        executeTaskForTest(newTask("app3", 30));
-
-        verifyProgressUpdate(30, 5L, 6L, 7L);
-    }
-
-    @Test
-    public void testSessionUpdate_widgets_updated() throws Exception {
-        executeTaskForTest(newTask("app4", 30));
-
-        verifyProgressUpdate(30, 8L, 9L);
-    }
-
-    private void verifyProgressUpdate(int progress, Long... idsUpdated) {
-        HashSet<Long> updates = new HashSet<>(Arrays.asList(idsUpdated));
-        for (ItemInfo info : bgDataModel.itemsIdMap) {
-            if (info instanceof ShortcutInfo) {
-                assertEquals(updates.contains(info.id) ? progress: 0,
-                        ((ShortcutInfo) info).getInstallProgress());
-            } else {
-                assertEquals(updates.contains(info.id) ? progress: -1,
-                        ((LauncherAppWidgetInfo) info).installProgress);
-            }
-        }
-    }
-}
diff --git a/tests/src/com/android/launcher3/popup/PopupPopulatorTest.java b/tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
deleted file mode 100644
index 9a89b1b..0000000
--- a/tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
+++ /dev/null
@@ -1,172 +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.popup;
-
-import android.content.pm.ShortcutInfo;
-import android.support.test.runner.AndroidJUnit4;
-
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS;
-import static com.android.launcher3.popup.PopupPopulator.NUM_DYNAMIC;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-/**
- * Tests the sorting and filtering of shortcuts in {@link PopupPopulator}.
- */
-@RunWith(AndroidJUnit4.class)
-public class PopupPopulatorTest {
-
-    @Test
-    public void testSortAndFilterShortcuts() {
-        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 0), 3, 0);
-        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(0, 3), 0, 3);
-        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(5, 0), MAX_SHORTCUTS, 0);
-        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(0, 5), 0, MAX_SHORTCUTS);
-        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 3),
-                MAX_SHORTCUTS - NUM_DYNAMIC, NUM_DYNAMIC);
-        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(5, 5),
-                MAX_SHORTCUTS - NUM_DYNAMIC, NUM_DYNAMIC);
-        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(5, 1), MAX_SHORTCUTS - 1, 1);
-        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(1, 5), 1, MAX_SHORTCUTS - 1);
-        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(5, 3),
-                MAX_SHORTCUTS - NUM_DYNAMIC, NUM_DYNAMIC);
-        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 5),
-                MAX_SHORTCUTS - NUM_DYNAMIC, NUM_DYNAMIC);
-    }
-
-    @Test
-    public void testDeDupeShortcutId() {
-        // Successfully remove one of the shortcuts
-        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 0), 2, 0, generateId(true, 1));
-        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(0, 3), 0, 2, generateId(false, 1));
-        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(2, 2), 2, 1, generateId(false, 1));
-        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(2, 2), 1, 2, generateId(true, 1));
-        // Successfully keep all shortcuts when id doesn't exist
-        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 0), 3, 0, generateId(false, 1));
-        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 0), 3, 0, generateId(true, 4));
-        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(2, 2), 2, 2, generateId(false, 4));
-        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(2, 2), 2, 2, generateId(true, 4));
-    }
-
-    private String generateId(boolean isStatic, int rank) {
-        return (isStatic ? "static" : "dynamic") + rank;
-    }
-
-    private void filterShortcutsAndAssertNumStaticAndDynamic(
-            List<ShortcutInfoCompat> shortcuts, int expectedStatic, int expectedDynamic) {
-        filterShortcutsAndAssertNumStaticAndDynamic(shortcuts, expectedStatic, expectedDynamic, null);
-    }
-
-    private void filterShortcutsAndAssertNumStaticAndDynamic(List<ShortcutInfoCompat> shortcuts,
-            int expectedStatic, int expectedDynamic, String shortcutIdToRemove) {
-        Collections.shuffle(shortcuts);
-        List<ShortcutInfoCompat> filteredShortcuts = PopupPopulator.sortAndFilterShortcuts(
-                shortcuts, shortcutIdToRemove);
-        assertIsSorted(filteredShortcuts);
-
-        int numStatic = 0;
-        int numDynamic = 0;
-        for (ShortcutInfoCompat shortcut : filteredShortcuts) {
-            if (shortcut.isDeclaredInManifest()) {
-                numStatic++;
-            }
-            if (shortcut.isDynamic()) {
-                numDynamic++;
-            }
-        }
-        assertEquals(expectedStatic, numStatic);
-        assertEquals(expectedDynamic, numDynamic);
-    }
-
-    private void assertIsSorted(List<ShortcutInfoCompat> shortcuts) {
-        int lastStaticRank = -1;
-        int lastDynamicRank = -1;
-        boolean hasSeenDynamic = false;
-        for (ShortcutInfoCompat shortcut : shortcuts) {
-            int rank = shortcut.getRank();
-            if (shortcut.isDeclaredInManifest()) {
-                assertFalse("Static shortcuts should come before all dynamic shortcuts.",
-                        hasSeenDynamic);
-                assertTrue(rank > lastStaticRank);
-                lastStaticRank = rank;
-            }
-            if (shortcut.isDynamic()) {
-                hasSeenDynamic = true;
-                assertTrue(rank > lastDynamicRank);
-                lastDynamicRank = rank;
-            }
-        }
-    }
-
-    private List<ShortcutInfoCompat> createShortcutsList(int numStatic, int numDynamic) {
-        List<ShortcutInfoCompat> shortcuts = new ArrayList<>();
-        for (int i = 0; i < numStatic; i++) {
-            shortcuts.add(new Shortcut(true, i));
-        }
-        for (int i = 0; i < numDynamic; i++) {
-            shortcuts.add(new Shortcut(false, i));
-        }
-        return shortcuts;
-    }
-
-    private class Shortcut extends ShortcutInfoCompat {
-        private boolean mIsStatic;
-        private int mRank;
-        private String mId;
-
-        public Shortcut(ShortcutInfo shortcutInfo) {
-            super(shortcutInfo);
-        }
-
-        public Shortcut(boolean isStatic, int rank) {
-            this(null);
-            mIsStatic = isStatic;
-            mRank = rank;
-            mId = generateId(isStatic, rank);
-        }
-
-        @Override
-        public boolean isDeclaredInManifest() {
-            return mIsStatic;
-        }
-
-        @Override
-        public boolean isDynamic() {
-            return !mIsStatic;
-        }
-
-        @Override
-        public int getRank() {
-            return mRank;
-        }
-
-        @Override
-        public String getId() {
-            return mId;
-        }
-    }
-}
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
index 5d417b5..6fa8d62 100644
--- a/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
+++ b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
@@ -3,9 +3,9 @@
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.MediumTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.android.launcher3.LauncherProvider.DatabaseHelper;
 import com.android.launcher3.LauncherSettings.Favorites;
@@ -41,18 +41,34 @@
         // Verify item add
         assertEquals(5, getCount(db, "select * from favorites where profileId = 42"));
 
-        new RestoreDbTask().migrateProfileId(db, 33);
+        new RestoreDbTask().migrateProfileId(db, 42, 33);
 
         // verify data migrated
         assertEquals(0, getCount(db, "select * from favorites where profileId = 42"));
         assertEquals(5, getCount(db, "select * from favorites where profileId = 33"));
+    }
+
+    @Test
+    public void testChangeDefaultColumn() throws Exception {
+        SQLiteDatabase db = new MyDatabaseHelper(42).getWritableDatabase();
+        // Add some dummy data
+        for (int i = 0; i < 5; i++) {
+            ContentValues values = new ContentValues();
+            values.put(Favorites._ID, i);
+            values.put(Favorites.TITLE, "item " + i);
+            db.insert(Favorites.TABLE_NAME, null, values);
+        }
+        // Verify default column is 42
+        assertEquals(5, getCount(db, "select * from favorites where profileId = 42"));
+
+        new RestoreDbTask().changeDefaultColumn(db, 33);
 
         // Verify default value changed
         ContentValues values = new ContentValues();
         values.put(Favorites._ID, 100);
         values.put(Favorites.TITLE, "item 100");
         db.insert(Favorites.TABLE_NAME, null, values);
-        assertEquals(6, getCount(db, "select * from favorites where profileId = 33"));
+        assertEquals(1, getCount(db, "select * from favorites where profileId = 33"));
     }
 
     private int getCount(SQLiteDatabase db, String sql) {
diff --git a/tests/src/com/android/launcher3/testcomponent/BaseTestingActivity.java b/tests/src/com/android/launcher3/testcomponent/BaseTestingActivity.java
index 904590c..9c6d102 100644
--- a/tests/src/com/android/launcher3/testcomponent/BaseTestingActivity.java
+++ b/tests/src/com/android/launcher3/testcomponent/BaseTestingActivity.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.graphics.Color;
 import android.os.Bundle;
 import android.util.TypedValue;
 import android.view.View;
@@ -63,6 +64,7 @@
         mView = new LinearLayout(this);
         mView.setPadding(mMargin, mMargin, mMargin, mMargin);
         mView.setOrientation(LinearLayout.VERTICAL);
+        mView.setBackgroundColor(Color.BLUE);
         setContentView(mView);
 
         registerReceiver(mCommandReceiver, new IntentFilter(mAction + SUFFIX_COMMAND));
diff --git a/tests/src/com/android/launcher3/testcomponent/TestCommandReceiver.java b/tests/src/com/android/launcher3/testcomponent/TestCommandReceiver.java
new file mode 100644
index 0000000..fa23b8d
--- /dev/null
+++ b/tests/src/com/android/launcher3/testcomponent/TestCommandReceiver.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.android.launcher3.testcomponent;
+
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import static android.content.pm.PackageManager.DONT_KILL_APP;
+import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.Instrumentation;
+import android.content.ComponentName;
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.util.Base64;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import androidx.test.InstrumentationRegistry;
+
+/**
+ * Content provider to receive commands from tests
+ */
+public class TestCommandReceiver extends ContentProvider {
+
+    public static final String ENABLE_TEST_LAUNCHER = "enable-test-launcher";
+    public static final String DISABLE_TEST_LAUNCHER = "disable-test-launcher";
+    public static final String KILL_PROCESS = "kill-process";
+
+    @Override
+    public boolean onCreate() {
+        return true;
+    }
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+            String sortOrder) {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
+    @Override
+    public Bundle call(String method, String arg, Bundle extras) {
+        switch (method) {
+            case ENABLE_TEST_LAUNCHER: {
+                getContext().getPackageManager().setComponentEnabledSetting(
+                        new ComponentName(getContext(), TestLauncherActivity.class),
+                        COMPONENT_ENABLED_STATE_ENABLED, DONT_KILL_APP);
+                return null;
+            }
+            case DISABLE_TEST_LAUNCHER: {
+                getContext().getPackageManager().setComponentEnabledSetting(
+                        new ComponentName(getContext(), TestLauncherActivity.class),
+                        COMPONENT_ENABLED_STATE_DISABLED, DONT_KILL_APP);
+                return null;
+            }
+            case KILL_PROCESS: {
+                ((ActivityManager) getContext().getSystemService(Activity.ACTIVITY_SERVICE)).
+                        killBackgroundProcesses(arg);
+                return null;
+            }
+        }
+        return super.call(method, arg, extras);
+    }
+
+    public static Bundle callCommand(String command) {
+        return callCommand(command, null);
+    }
+
+    public static Bundle callCommand(String command, String arg) {
+        Instrumentation inst = InstrumentationRegistry.getInstrumentation();
+        Uri uri = Uri.parse("content://" + inst.getContext().getPackageName() + ".commands");
+        return inst.getTargetContext().getContentResolver().call(uri, command, arg, null);
+    }
+
+    @Override
+    public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
+        String path = Base64.encodeToString(uri.getPath().getBytes(),
+                Base64.NO_CLOSE | Base64.NO_PADDING | Base64.NO_WRAP);
+        File file = new File(getContext().getCacheDir(), path);
+        if (!file.exists()) {
+            // Create an empty file so that we can pass its descriptor
+            try {
+                file.createNewFile();
+            } catch (IOException e) { }
+        }
+
+        return ParcelFileDescriptor.open(file, MODE_READ_WRITE);
+    }
+}
diff --git a/tests/src/com/android/launcher3/testcomponent/TestLauncherActivity.java b/tests/src/com/android/launcher3/testcomponent/TestLauncherActivity.java
new file mode 100644
index 0000000..357a232
--- /dev/null
+++ b/tests/src/com/android/launcher3/testcomponent/TestLauncherActivity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.android.launcher3.testcomponent;
+
+import static android.content.Intent.ACTION_MAIN;
+import static android.content.Intent.CATEGORY_LAUNCHER;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
+
+import android.app.LauncherActivity;
+import android.content.Intent;
+
+public class TestLauncherActivity extends LauncherActivity {
+
+    @Override
+    protected Intent getTargetIntent() {
+        return new Intent(ACTION_MAIN, null)
+                .addCategory(CATEGORY_LAUNCHER)
+                .addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+    }
+}
diff --git a/tests/src/com/android/launcher3/touch/SwipeDetectorTest.java b/tests/src/com/android/launcher3/touch/SwipeDetectorTest.java
index ff83131..e042357 100644
--- a/tests/src/com/android/launcher3/touch/SwipeDetectorTest.java
+++ b/tests/src/com/android/launcher3/touch/SwipeDetectorTest.java
@@ -15,11 +15,14 @@
  */
 package com.android.launcher3.touch;
 
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyFloat;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
 import android.util.Log;
-import android.view.MotionEvent;
 import android.view.ViewConfiguration;
 
 import com.android.launcher3.testcomponent.TouchEventGenerator;
@@ -30,10 +33,9 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyFloat;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -51,25 +53,40 @@
     @Mock
     private SwipeDetector.Listener mMockListener;
 
+    @Mock
+    private ViewConfiguration mMockConfig;
+
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
-        mGenerator = new TouchEventGenerator(new TouchEventGenerator.Listener() {
-            @Override
-            public void onTouchEvent(MotionEvent event) {
-                mDetector.onTouchEvent(event);
-            }
-        });
+        mGenerator = new TouchEventGenerator((ev) -> mDetector.onTouchEvent(ev));
+        ViewConfiguration orgConfig = ViewConfiguration
+                .get(InstrumentationRegistry.getTargetContext());
+        doReturn(orgConfig.getScaledMaximumFlingVelocity()).when(mMockConfig)
+                .getScaledMaximumFlingVelocity();
 
-        mDetector = new SwipeDetector(mTouchSlop, mMockListener, SwipeDetector.VERTICAL);
+        mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.VERTICAL, false);
         mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_BOTH, false);
-        mTouchSlop = ViewConfiguration.get(InstrumentationRegistry.getTargetContext())
-                .getScaledTouchSlop();
+        mTouchSlop = orgConfig.getScaledTouchSlop();
+        doReturn(mTouchSlop).when(mMockConfig).getScaledTouchSlop();
+
         L("mTouchSlop=", mTouchSlop);
     }
 
     @Test
-    public void testDragStart_vertical() throws Exception {
+    public void testDragStart_verticalPositive() {
+        mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.VERTICAL, false);
+        mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_POSITIVE, false);
+        mGenerator.put(0, 100, 100);
+        mGenerator.move(0, 100, 100 - mTouchSlop);
+        // TODO: actually calculate the following parameters and do exact value checks.
+        verify(mMockListener).onDragStart(anyBoolean());
+    }
+
+    @Test
+    public void testDragStart_verticalNegative() {
+        mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.VERTICAL, false);
+        mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_NEGATIVE, false);
         mGenerator.put(0, 100, 100);
         mGenerator.move(0, 100, 100 + mTouchSlop);
         // TODO: actually calculate the following parameters and do exact value checks.
@@ -77,7 +94,7 @@
     }
 
     @Test
-    public void testDragStart_failed() throws Exception {
+    public void testDragStart_failed() {
         mGenerator.put(0, 100, 100);
         mGenerator.move(0, 100 + mTouchSlop, 100);
         // TODO: actually calculate the following parameters and do exact value checks.
@@ -85,9 +102,9 @@
     }
 
     @Test
-    public void testDragStart_horizontal() throws Exception {
-        mDetector = new SwipeDetector(mTouchSlop, mMockListener, SwipeDetector.HORIZONTAL);
-        mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_BOTH, false);
+    public void testDragStart_horizontalPositive() {
+        mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.HORIZONTAL, false);
+        mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_POSITIVE, false);
 
         mGenerator.put(0, 100, 100);
         mGenerator.move(0, 100 + mTouchSlop, 100);
@@ -96,15 +113,48 @@
     }
 
     @Test
-    public void testDrag() throws Exception {
+    public void testDragStart_horizontalNegative() {
+        mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.HORIZONTAL, false);
+        mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_NEGATIVE, false);
+
         mGenerator.put(0, 100, 100);
-        mGenerator.move(0, 100, 100 + mTouchSlop);
+        mGenerator.move(0, 100 - mTouchSlop, 100);
         // TODO: actually calculate the following parameters and do exact value checks.
-        verify(mMockListener).onDrag(anyFloat(), anyFloat());
+        verify(mMockListener).onDragStart(anyBoolean());
     }
 
     @Test
-    public void testDragEnd() throws Exception {
+    public void testDragStart_horizontalRtlPositive() {
+        mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.HORIZONTAL, true);
+        mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_POSITIVE, false);
+
+        mGenerator.put(0, 100, 100);
+        mGenerator.move(0, 100 - mTouchSlop, 100);
+        // TODO: actually calculate the following parameters and do exact value checks.
+        verify(mMockListener).onDragStart(anyBoolean());
+    }
+
+    @Test
+    public void testDragStart_horizontalRtlNegative() {
+        mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.HORIZONTAL, true);
+        mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_NEGATIVE, false);
+
+        mGenerator.put(0, 100, 100);
+        mGenerator.move(0, 100 + mTouchSlop, 100);
+        // TODO: actually calculate the following parameters and do exact value checks.
+        verify(mMockListener).onDragStart(anyBoolean());
+    }
+
+    @Test
+    public void testDrag() {
+        mGenerator.put(0, 100, 100);
+        mGenerator.move(0, 100, 100 + mTouchSlop);
+        // TODO: actually calculate the following parameters and do exact value checks.
+        verify(mMockListener).onDrag(anyFloat(), anyObject());
+    }
+
+    @Test
+    public void testDragEnd() {
         mGenerator.put(0, 100, 100);
         mGenerator.move(0, 100, 100 + mTouchSlop);
         mGenerator.move(0, 100, 100 + mTouchSlop * 2);
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index f16f514..4a0ca5c 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -15,6 +15,15 @@
  */
 package com.android.launcher3.ui;
 
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import static java.lang.System.exit;
+
 import android.app.Instrumentation;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -22,40 +31,52 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.LauncherActivityInfo;
-import android.graphics.Point;
+import android.content.pm.PackageManager;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.SystemClock;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.BySelector;
-import android.support.test.uiautomator.Direction;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.Until;
 import android.util.Log;
-import android.view.MotionEvent;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.Direction;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
+
+import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.LauncherState;
 import com.android.launcher3.MainThreadExecutor;
-import com.android.launcher3.R;
-import com.android.launcher3.compat.AppWidgetManagerCompat;
+import com.android.launcher3.ResourceUtils;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.LauncherAppsCompat;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.testcomponent.AppWidgetNoConfig;
-import com.android.launcher3.testcomponent.AppWidgetWithConfig;
+import com.android.launcher3.model.AppLaunchTracker;
+import com.android.launcher3.tapl.LauncherInstrumentation;
+import com.android.launcher3.tapl.TestHelpers;
+import com.android.launcher3.util.Wait;
+import com.android.launcher3.util.rule.FailureWatcher;
+import com.android.launcher3.util.rule.LauncherActivityRule;
+import com.android.launcher3.util.rule.ShellCommandRule;
 
+import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
 
-import java.util.Locale;
+import java.io.IOException;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 import java.util.concurrent.Callable;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
-
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
+import java.util.function.Consumer;
+import java.util.function.Function;
 
 /**
  * Base class for all instrumentation tests providing various utility methods.
@@ -65,23 +86,80 @@
     public static final long DEFAULT_ACTIVITY_TIMEOUT = TimeUnit.SECONDS.toMillis(10);
     public static final long DEFAULT_BROADCAST_TIMEOUT_SECS = 5;
 
-    public static final long SHORT_UI_TIMEOUT= 300;
-    public static final long DEFAULT_UI_TIMEOUT = 3000;
-    public static final long LARGE_UI_TIMEOUT = 10000;
-    public static final long DEFAULT_WORKER_TIMEOUT_SECS = 5;
+    public static final long SHORT_UI_TIMEOUT = 300;
+    public static final long DEFAULT_UI_TIMEOUT = 10000;
+    private static final String TAG = "AbstractLauncherUiTest";
 
     protected MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
-    protected UiDevice mDevice;
+    protected final UiDevice mDevice = UiDevice.getInstance(getInstrumentation());
+    protected final LauncherInstrumentation mLauncher =
+            new LauncherInstrumentation(getInstrumentation());
     protected Context mTargetContext;
     protected String mTargetPackage;
 
-    private static final String TAG = "AbstractLauncherUiTest";
+    protected AbstractLauncherUiTest() {
+        try {
+            mDevice.setOrientationNatural();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+        if (TestHelpers.isInLauncherProcess()) Utilities.enableRunningInTestHarnessForTests();
+    }
+
+    protected final LauncherActivityRule mActivityMonitor = new LauncherActivityRule();
+
+    @Rule
+    public ShellCommandRule mDefaultLauncherRule =
+            TestHelpers.isInLauncherProcess() ? ShellCommandRule.setDefaultLauncher() : null;
+
+    @Rule
+    public ShellCommandRule mDisableHeadsUpNotification =
+            ShellCommandRule.disableHeadsUpNotification();
+
+    // Annotation for tests that need to be run in portrait and landscape modes.
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target(ElementType.METHOD)
+    protected @interface PortraitLandscape {
+    }
+
+    protected TestRule getRulesInsideActivityMonitor() {
+        return RuleChain.
+                outerRule(new PortraitLandscapeRunner(this)).
+                around(new FailureWatcher(mDevice));
+    }
+
+    @Rule
+    public TestRule mOrderSensitiveRules = RuleChain.
+            outerRule(mActivityMonitor).
+            around(getRulesInsideActivityMonitor());
+
+    public UiDevice getDevice() {
+        return mDevice;
+    }
 
     @Before
     public void setUp() throws Exception {
-        mDevice = UiDevice.getInstance(getInstrumentation());
+        // Disable app tracker
+        AppLaunchTracker.INSTANCE.initializeForTesting(new AppLaunchTracker());
+
         mTargetContext = InstrumentationRegistry.getTargetContext();
         mTargetPackage = mTargetContext.getPackageName();
+        // Unlock the phone
+        mDevice.executeShellCommand("input keyevent 82");
+    }
+
+    @After
+    public void verifyLauncherState() {
+        try {
+            // Limits UI tests affecting tests running after them.
+            waitForModelLoaded();
+        } catch (Throwable t) {
+            Log.e(TAG,
+                    "Couldn't deinit after a test, exiting tests, see logs for failures that "
+                            + "could have caused this",
+                    t);
+            exit(1);
+        }
     }
 
     protected void lockRotation(boolean naturalOrientation) throws RemoteException {
@@ -92,122 +170,39 @@
         }
     }
 
-    protected Instrumentation getInstrumentation() {
-        return InstrumentationRegistry.getInstrumentation();
-    }
-
-    /**
-     * Opens all apps and returns the recycler view
-     */
-    protected UiObject2 openAllApps() {
-        mDevice.waitForIdle();
-        if (FeatureFlags.NO_ALL_APPS_ICON) {
-            UiObject2 hotseat = mDevice.wait(
-                    Until.findObject(getSelectorForId(R.id.hotseat)), 2500);
-            Point start = hotseat.getVisibleCenter();
-            int endY = (int) (mDevice.getDisplayHeight() * 0.1f);
-            // 100 px/step
-            mDevice.swipe(start.x, start.y, start.x, endY, (start.y - endY) / 100);
-
+    protected void clearLauncherData() throws IOException {
+        if (TestHelpers.isInLauncherProcess()) {
+            LauncherSettings.Settings.call(mTargetContext.getContentResolver(),
+                    LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
+            resetLoaderState();
         } else {
-            mDevice.wait(Until.findObject(
-                    By.desc(mTargetContext.getString(R.string.all_apps_button_label))),
-                    DEFAULT_UI_TIMEOUT).click();
+            mDevice.executeShellCommand("pm clear " + mDevice.getLauncherPackageName());
         }
-        return findViewById(R.id.apps_list_view);
-    }
-
-    /**
-     * Opens widget tray and returns the recycler view.
-     */
-    protected UiObject2 openWidgetsTray() {
-        mDevice.pressMenu(); // Enter overview mode.
-        mDevice.wait(Until.findObject(
-                By.text(mTargetContext.getString(R.string.widget_button_text))), DEFAULT_UI_TIMEOUT).click();
-        return findViewById(R.id.widgets_list_view);
     }
 
     /**
      * Scrolls the {@param container} until it finds an object matching {@param condition}.
+     *
      * @return the matching object.
      */
     protected UiObject2 scrollAndFind(UiObject2 container, BySelector condition) {
-        do {
+        final int margin = ResourceUtils.getNavbarSize(
+                ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, mLauncher.getResources()) + 1;
+        container.setGestureMargins(0, 0, 0, margin);
+
+        int i = 0;
+        for (; ; ) {
             // findObject can only execute after spring settles.
             mDevice.wait(Until.findObject(condition), SHORT_UI_TIMEOUT);
             UiObject2 widget = container.findObject(condition);
-            if (widget != null) {
+            if (widget != null && widget.getVisibleBounds().intersects(
+                    0, 0, mDevice.getDisplayWidth(),
+                    mDevice.getDisplayHeight() - margin)) {
                 return widget;
             }
-        } while (container.scroll(Direction.DOWN, 1f));
-        return container.findObject(condition);
-    }
-
-    /**
-     * Drags an icon to the center of homescreen.
-     * @param icon  object that is either app icon or shortcut icon
-     */
-    protected void dragToWorkspace(UiObject2 icon, boolean expectedToShowShortcuts) {
-        Point center = icon.getVisibleCenter();
-
-        // Action Down
-        sendPointer(MotionEvent.ACTION_DOWN, center);
-
-        UiObject2 dragLayer = findViewById(R.id.drag_layer);
-
-        if (expectedToShowShortcuts) {
-            // Make sure shortcuts show up, and then move a bit to hide them.
-            assertNotNull(findViewById(R.id.deep_shortcuts_container));
-
-            Point moveLocation = new Point(center);
-            int distanceToMove = mTargetContext.getResources().getDimensionPixelSize(
-                    R.dimen.deep_shortcuts_start_drag_threshold) + 50;
-            if (moveLocation.y - distanceToMove >= dragLayer.getVisibleBounds().top) {
-                moveLocation.y -= distanceToMove;
-            } else {
-                moveLocation.y += distanceToMove;
-            }
-            movePointer(center, moveLocation);
-
-            assertNull(findViewById(R.id.deep_shortcuts_container));
+            if (++i > 40) fail("Too many attempts");
+            container.scroll(Direction.DOWN, 1f);
         }
-
-        // Wait until Remove/Delete target is visible
-        assertNotNull(findViewById(R.id.delete_target_text));
-
-        Point moveLocation = dragLayer.getVisibleCenter();
-
-        // Move to center
-        movePointer(center, moveLocation);
-        sendPointer(MotionEvent.ACTION_UP, center);
-
-        // Wait until remove target is gone.
-        mDevice.wait(Until.gone(getSelectorForId(R.id.delete_target_text)), DEFAULT_UI_TIMEOUT);
-    }
-
-    private void movePointer(Point from, Point to) {
-        while(!from.equals(to)) {
-            from.x = getNextMoveValue(to.x, from.x);
-            from.y = getNextMoveValue(to.y, from.y);
-            sendPointer(MotionEvent.ACTION_MOVE, from);
-        }
-    }
-
-    private int getNextMoveValue(int targetValue, int oldValue) {
-        if (targetValue - oldValue > 10) {
-            return oldValue + 10;
-        } else if (targetValue - oldValue < -10) {
-            return oldValue - 10;
-        } else {
-            return targetValue;
-        }
-    }
-
-    protected void sendPointer(int action, Point point) {
-        MotionEvent event = MotionEvent.obtain(SystemClock.uptimeMillis(),
-                SystemClock.uptimeMillis(), action, point.x, point.y, 0);
-        getInstrumentation().sendPointerSync(event);
-        event.recycle();
     }
 
     /**
@@ -223,15 +218,19 @@
 
     protected void resetLoaderState() {
         try {
-            mMainThreadExecutor.execute(new Runnable() {
-                @Override
-                public void run() {
-                    LauncherAppState.getInstance(mTargetContext).getModel().forceReload();
-                }
-            });
+            mMainThreadExecutor.execute(
+                    () -> LauncherAppState.getInstance(mTargetContext).getModel().forceReload());
         } catch (Throwable t) {
             throw new IllegalArgumentException(t);
         }
+        waitForModelLoaded();
+    }
+
+    protected void waitForModelLoaded() {
+        waitForLauncherCondition("Launcher model didn't load", launcher -> {
+            final LauncherModel model = LauncherAppState.getInstance(mTargetContext).getModel();
+            return model.getCallback() == null || model.isModelLoaded();
+        });
     }
 
     /**
@@ -245,35 +244,54 @@
         }
     }
 
-    /**
-     * Finds a widget provider which can fit on the home screen.
-     * @param hasConfigureScreen if true, a provider with a config screen is returned.
-     */
-    protected LauncherAppWidgetProviderInfo findWidgetProvider(final boolean hasConfigureScreen) {
-        LauncherAppWidgetProviderInfo info =
-                getOnUiThread(new Callable<LauncherAppWidgetProviderInfo>() {
-            @Override
-            public LauncherAppWidgetProviderInfo call() throws Exception {
-                ComponentName cn = new ComponentName(getInstrumentation().getContext(),
-                        hasConfigureScreen ? AppWidgetWithConfig.class : AppWidgetNoConfig.class);
-                Log.d(TAG, "findWidgetProvider componentName=" + cn.flattenToString());
-                return AppWidgetManagerCompat.getInstance(mTargetContext)
-                        .findProvider(cn, Process.myUserHandle());
-            }
+    protected <T> T getFromLauncher(Function<Launcher, T> f) {
+        if (!TestHelpers.isInLauncherProcess()) return null;
+        return getOnUiThread(() -> f.apply(mActivityMonitor.getActivity()));
+    }
+
+    protected void executeOnLauncher(Consumer<Launcher> f) {
+        getFromLauncher(launcher -> {
+            f.accept(launcher);
+            return null;
         });
-        if (info == null) {
-            throw new IllegalArgumentException("No valid widget provider");
-        }
-        return info;
     }
 
-    protected UiObject2 findViewById(int id) {
-        return mDevice.wait(Until.findObject(getSelectorForId(id)), DEFAULT_UI_TIMEOUT);
+    // Cannot be used in TaplTests between a Tapl call injecting a gesture and a tapl call expecting
+    // the results of that gesture because the wait can hide flakeness.
+    protected void waitForState(String message, LauncherState state) {
+        waitForLauncherCondition(message,
+                launcher -> launcher.getStateManager().getCurrentStableState() == state);
     }
 
-    protected BySelector getSelectorForId(int id) {
-        String name = mTargetContext.getResources().getResourceEntryName(id);
-        return By.res(mTargetPackage, name);
+    protected void waitForResumed(String message) {
+        waitForLauncherCondition(message, launcher -> launcher.hasBeenResumed());
+    }
+
+    // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
+    // flakiness.
+    protected void waitForLauncherCondition(String message, Function<Launcher, Boolean> condition) {
+        waitForLauncherCondition(message, condition, DEFAULT_ACTIVITY_TIMEOUT);
+    }
+
+    // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
+    // flakiness.
+    protected void waitForLauncherCondition(
+            String message, Function<Launcher, Boolean> condition, long timeout) {
+        if (!TestHelpers.isInLauncherProcess()) return;
+        Wait.atMost(message, () -> getFromLauncher(condition), timeout);
+    }
+
+    // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
+    // flakiness.
+    protected void waitForLauncherCondition(
+            String message,
+            Runnable testThreadAction, Function<Launcher, Boolean> condition,
+            long timeout) {
+        if (!TestHelpers.isInLauncherProcess()) return;
+        Wait.atMost(message, () -> {
+            testThreadAction.run();
+            return getFromLauncher(condition);
+        }, timeout);
     }
 
     protected LauncherActivityInfo getSettingsApp() {
@@ -282,11 +300,6 @@
                         Process.myUserHandle()).get(0);
     }
 
-    protected LauncherActivityInfo getChromeApp() {
-        return LauncherAppsCompat.getInstance(mTargetContext)
-                .getActivityList("com.android.chrome", Process.myUserHandle()).get(0);
-    }
-
     /**
      * Broadcast receiver which blocks until the result is received.
      */
@@ -316,4 +329,62 @@
             return intent == null ? null : (Intent) intent.getParcelableExtra(Intent.EXTRA_INTENT);
         }
     }
+
+    protected void startAppFast(String packageName) {
+        final Instrumentation instrumentation = getInstrumentation();
+        final Intent intent = instrumentation.getContext().getPackageManager().
+                getLaunchIntentForPackage(packageName);
+        intent.addCategory(Intent.CATEGORY_LAUNCHER);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        instrumentation.getTargetContext().startActivity(intent);
+        assertTrue(packageName + " didn't start",
+                mDevice.wait(Until.hasObject(By.pkg(packageName).depth(0)), DEFAULT_UI_TIMEOUT));
+    }
+
+    protected void startTestActivity(int activityNumber) {
+        final String packageName = getAppPackageName();
+        final Instrumentation instrumentation = getInstrumentation();
+        final Intent intent = instrumentation.getContext().getPackageManager().
+                getLaunchIntentForPackage(packageName);
+        intent.addCategory(Intent.CATEGORY_LAUNCHER);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.setComponent(new ComponentName(packageName,
+                "com.android.launcher3.tests.Activity" + activityNumber));
+        instrumentation.getTargetContext().startActivity(intent);
+        assertTrue(packageName + " didn't start",
+                mDevice.wait(
+                        Until.hasObject(By.pkg(packageName).text("TestActivity" + activityNumber)),
+                        DEFAULT_UI_TIMEOUT));
+    }
+
+    public static String resolveSystemApp(String category) {
+        return getInstrumentation().getContext().getPackageManager().resolveActivity(
+                new Intent(Intent.ACTION_MAIN).addCategory(category),
+                PackageManager.MATCH_SYSTEM_ONLY).
+                activityInfo.packageName;
+    }
+
+    protected void closeLauncherActivity() {
+        // Destroy Launcher activity.
+        executeOnLauncher(launcher -> {
+            if (launcher != null) {
+                launcher.finish();
+            }
+        });
+        waitForLauncherCondition(
+                "Launcher still active", launcher -> launcher == null, DEFAULT_UI_TIMEOUT);
+    }
+
+    protected boolean isInBackground(Launcher launcher) {
+        return !launcher.hasBeenResumed();
+    }
+
+    protected boolean isInState(LauncherState state) {
+        if (!TestHelpers.isInLauncherProcess()) return true;
+        return getFromLauncher(launcher -> launcher.getStateManager().getState() == state);
+    }
+
+    protected int getAllAppsScroll(Launcher launcher) {
+        return launcher.getAppsView().getActiveRecyclerView().getCurrentScrollY();
+    }
 }
diff --git a/tests/src/com/android/launcher3/ui/AllAppsAppLaunchTest.java b/tests/src/com/android/launcher3/ui/AllAppsAppLaunchTest.java
deleted file mode 100644
index b95a850..0000000
--- a/tests/src/com/android/launcher3/ui/AllAppsAppLaunchTest.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package com.android.launcher3.ui;
-
-import android.content.pm.LauncherActivityInfo;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.Until;
-
-import com.android.launcher3.util.Condition;
-import com.android.launcher3.util.Wait;
-import com.android.launcher3.util.rule.LauncherActivityRule;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import static org.junit.Assert.assertTrue;
-
-/**
- * Test for verifying apps is launched from all-apps
- */
-@LargeTest
-@RunWith(AndroidJUnit4.class)
-public class AllAppsAppLaunchTest extends AbstractLauncherUiTest {
-
-    @Rule public LauncherActivityRule mActivityMonitor = new LauncherActivityRule();
-
-    @Test
-    public void testAppLauncher_portrait() throws Exception {
-        lockRotation(true);
-        performTest();
-    }
-
-    @Test
-    public void testAppLauncher_landscape() throws Exception {
-        lockRotation(false);
-        performTest();
-    }
-
-    private void performTest() throws Exception {
-        mActivityMonitor.startLauncher();
-
-        LauncherActivityInfo testApp = getChromeApp();
-
-        // Open all apps and wait for load complete
-        final UiObject2 appsContainer = openAllApps();
-        assertTrue(Wait.atMost(Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT));
-
-        // Open app and verify app launched
-        scrollAndFind(appsContainer, By.text(testApp.getLabel().toString())).click();
-        assertTrue(mDevice.wait(Until.hasObject(By.pkg(
-                testApp.getComponentName().getPackageName()).depth(0)), DEFAULT_UI_TIMEOUT));
-    }
-}
diff --git a/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java b/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java
deleted file mode 100644
index 00f30ad..0000000
--- a/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package com.android.launcher3.ui;
-
-import android.content.pm.LauncherActivityInfo;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.Until;
-
-import com.android.launcher3.util.Condition;
-import com.android.launcher3.util.Wait;
-import com.android.launcher3.util.rule.LauncherActivityRule;
-import com.android.launcher3.util.rule.ShellCommandRule;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import static org.junit.Assert.assertTrue;
-
-/**
- * Test for dragging an icon from all-apps to homescreen.
- */
-@LargeTest
-@RunWith(AndroidJUnit4.class)
-public class AllAppsIconToHomeTest extends AbstractLauncherUiTest {
-
-    @Rule public LauncherActivityRule mActivityMonitor = new LauncherActivityRule();
-    @Rule public ShellCommandRule mDefaultLauncherRule = ShellCommandRule.setDefaultLauncher();
-
-    @Test
-    public void testDragIcon_portrait() throws Throwable {
-        lockRotation(true);
-        performTest();
-    }
-
-    @Test
-    public void testDragIcon_landscape() throws Throwable {
-        lockRotation(false);
-        performTest();
-    }
-
-    private void performTest() throws Throwable {
-        LauncherActivityInfo settingsApp = getSettingsApp();
-
-        clearHomescreen();
-        mActivityMonitor.startLauncher();
-
-        // Open all apps and wait for load complete.
-        final UiObject2 appsContainer = openAllApps();
-        assertTrue(Wait.atMost(Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT));
-
-        // Drag icon to homescreen.
-        UiObject2 icon = scrollAndFind(appsContainer, By.text(settingsApp.getLabel().toString()));
-        dragToWorkspace(icon, true);
-
-        // Verify that the icon works on homescreen.
-        mDevice.findObject(By.text(settingsApp.getLabel().toString())).click();
-        assertTrue(mDevice.wait(Until.hasObject(By.pkg(
-                settingsApp.getComponentName().getPackageName()).depth(0)), DEFAULT_UI_TIMEOUT));
-    }
-}
diff --git a/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java b/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java
new file mode 100644
index 0000000..58c74ce
--- /dev/null
+++ b/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java
@@ -0,0 +1,129 @@
+/**
+ * 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.ui;
+
+import static org.junit.Assert.assertTrue;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.testcomponent.TestCommandReceiver;
+import com.android.launcher3.util.LauncherLayoutBuilder;
+import com.android.launcher3.util.rule.ShellCommandRule;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.OutputStreamWriter;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class DefaultLayoutProviderTest extends AbstractLauncherUiTest {
+
+    @Rule
+    public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
+
+    private static final String SETTINGS_APP = "com.android.settings";
+
+    private Context mContext;
+    private String mAuthority;
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mContext = InstrumentationRegistry.getContext();
+
+        PackageManager pm = mTargetContext.getPackageManager();
+        ProviderInfo pi = pm.getProviderInfo(new ComponentName(mContext,
+                TestCommandReceiver.class), 0);
+        mAuthority = pi.authority;
+    }
+
+    @Test
+    public void testCustomProfileLoaded_with_icon_on_hotseat() throws Exception {
+        writeLayout(new LauncherLayoutBuilder().atHotseat(0).putApp(SETTINGS_APP, SETTINGS_APP));
+
+        // Launch the home activity
+        mActivityMonitor.startLauncher();
+        waitForModelLoaded();
+
+        mLauncher.getWorkspace().getHotseatAppIcon(getSettingsApp().getLabel().toString());
+    }
+
+    @Test
+    public void testCustomProfileLoaded_with_widget() throws Exception {
+        // A non-restored widget with no config screen gets restored automatically.
+        LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
+
+        writeLayout(new LauncherLayoutBuilder().atWorkspace(0, 1, 0)
+                .putWidget(info.getComponent().getPackageName(),
+                        info.getComponent().getClassName(), 2, 2));
+
+        // Launch the home activity
+        mActivityMonitor.startLauncher();
+        waitForModelLoaded();
+
+        // Verify widget present
+        assertTrue("Widget is not present",
+                mLauncher.getWorkspace().tryGetWidget(info.label, DEFAULT_UI_TIMEOUT) != null);
+    }
+
+    @Test
+    public void testCustomProfileLoaded_with_folder() throws Exception {
+        writeLayout(new LauncherLayoutBuilder().atHotseat(0).putFolder(android.R.string.copy)
+                .addApp(SETTINGS_APP, SETTINGS_APP)
+                .addApp(SETTINGS_APP, SETTINGS_APP)
+                .addApp(SETTINGS_APP, SETTINGS_APP)
+                .build());
+
+        // Launch the home activity
+        mActivityMonitor.startLauncher();
+        waitForModelLoaded();
+
+        mLauncher.getWorkspace().getHotseatFolder("Folder: Copy");
+    }
+
+    @After
+    public void cleanup() throws Exception {
+        mDevice.executeShellCommand("settings delete secure launcher3.layout.provider");
+    }
+
+    private void writeLayout(LauncherLayoutBuilder builder) throws Exception {
+        mDevice.executeShellCommand("settings put secure launcher3.layout.provider " + mAuthority);
+        ParcelFileDescriptor pfd = mTargetContext.getContentResolver().openFileDescriptor(
+                Uri.parse("content://" + mAuthority + "/launcher_layout"), "w");
+
+        try (OutputStreamWriter writer = new OutputStreamWriter(new AutoCloseOutputStream(pfd))) {
+            builder.build(writer);
+        }
+        clearLauncherData();
+    }
+}
diff --git a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
new file mode 100644
index 0000000..0f36292
--- /dev/null
+++ b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
@@ -0,0 +1,63 @@
+package com.android.launcher3.ui;
+
+import android.view.Surface;
+
+import com.android.launcher3.tapl.TestHelpers;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+class PortraitLandscapeRunner implements TestRule {
+    private AbstractLauncherUiTest mTest;
+
+    public PortraitLandscapeRunner(AbstractLauncherUiTest test) {
+        mTest = test;
+    }
+
+    @Override
+    public Statement apply(Statement base, Description description) {
+        if (!TestHelpers.isInLauncherProcess() ||
+                description.getAnnotation(AbstractLauncherUiTest.PortraitLandscape.class) == null) {
+            return base;
+        }
+
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                try {
+                    mTest.mDevice.pressHome();
+                    mTest.waitForLauncherCondition("Launcher activity wasn't created",
+                            launcher -> launcher != null);
+
+                    mTest.executeOnLauncher(launcher ->
+                            launcher.getRotationHelper().forceAllowRotationForTesting(
+                                    true));
+
+                    evaluateInPortrait();
+                    evaluateInLandscape();
+                } finally {
+                    mTest.mDevice.setOrientationNatural();
+                    mTest.executeOnLauncher(launcher ->
+                            launcher.getRotationHelper().forceAllowRotationForTesting(
+                                    false));
+                    mTest.mLauncher.setExpectedRotation(Surface.ROTATION_0);
+                }
+            }
+
+            private void evaluateInPortrait() throws Throwable {
+                mTest.mDevice.setOrientationNatural();
+                mTest.mLauncher.setExpectedRotation(Surface.ROTATION_0);
+                base.evaluate();
+                mTest.getDevice().pressHome();
+            }
+
+            private void evaluateInLandscape() throws Throwable {
+                mTest.mDevice.setOrientationLeft();
+                mTest.mLauncher.setExpectedRotation(Surface.ROTATION_90);
+                base.evaluate();
+                mTest.getDevice().pressHome();
+            }
+        };
+    }
+}
diff --git a/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java b/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java
deleted file mode 100644
index 69f6c87..0000000
--- a/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java
+++ /dev/null
@@ -1,76 +0,0 @@
-package com.android.launcher3.ui;
-
-import android.content.pm.LauncherActivityInfo;
-import android.graphics.Point;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.Until;
-import android.view.MotionEvent;
-
-import com.android.launcher3.R;
-import com.android.launcher3.util.Condition;
-import com.android.launcher3.util.Wait;
-import com.android.launcher3.util.rule.LauncherActivityRule;
-import com.android.launcher3.util.rule.ShellCommandRule;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-/**
- * Test for verifying that shortcuts are shown and can be launched after long pressing an app
- */
-@LargeTest
-@RunWith(AndroidJUnit4.class)
-public class ShortcutsLaunchTest extends AbstractLauncherUiTest {
-
-    @Rule public LauncherActivityRule mActivityMonitor = new LauncherActivityRule();
-    @Rule public ShellCommandRule mDefaultLauncherRule = ShellCommandRule.setDefaultLauncher();
-
-    @Test
-    public void testAppLauncher_portrait() throws Exception {
-        lockRotation(true);
-        performTest();
-    }
-
-    @Test
-    public void testAppLauncher_landscape() throws Exception {
-        lockRotation(false);
-        performTest();
-    }
-
-    private void performTest() throws Exception {
-        mActivityMonitor.startLauncher();
-        LauncherActivityInfo testApp = getSettingsApp();
-
-        // Open all apps and wait for load complete
-        final UiObject2 appsContainer = openAllApps();
-        assertTrue(Wait.atMost(Condition.minChildCount(appsContainer, 2),
-                DEFAULT_UI_TIMEOUT));
-
-        // Find settings app and verify shortcuts appear when long pressed
-        UiObject2 icon = scrollAndFind(appsContainer, By.text(testApp.getLabel().toString()));
-        // Press icon center until shortcuts appear
-        Point iconCenter = icon.getVisibleCenter();
-        sendPointer(MotionEvent.ACTION_DOWN, iconCenter);
-        UiObject2 deepShortcutsContainer = findViewById(R.id.deep_shortcuts_container);
-        assertNotNull(deepShortcutsContainer);
-        sendPointer(MotionEvent.ACTION_UP, iconCenter);
-
-        // Verify that launching a shortcut opens a page with the same text
-        assertTrue(deepShortcutsContainer.getChildCount() > 0);
-
-        // Pick second children as it starts showing shortcuts.
-        UiObject2 shortcut = deepShortcutsContainer.getChildren().get(1)
-                .findObject(getSelectorForId(R.id.bubble_text));
-        shortcut.click();
-        assertTrue(mDevice.wait(Until.hasObject(By.pkg(
-                testApp.getComponentName().getPackageName())
-                .text(shortcut.getText())), DEFAULT_UI_TIMEOUT));
-    }
-}
diff --git a/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java b/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java
deleted file mode 100644
index fad06a6..0000000
--- a/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java
+++ /dev/null
@@ -1,81 +0,0 @@
-package com.android.launcher3.ui;
-
-import android.content.pm.LauncherActivityInfo;
-import android.graphics.Point;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.Until;
-import android.view.MotionEvent;
-
-import com.android.launcher3.R;
-import com.android.launcher3.util.Condition;
-import com.android.launcher3.util.Wait;
-import com.android.launcher3.util.rule.LauncherActivityRule;
-import com.android.launcher3.util.rule.ShellCommandRule;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-/**
- * Test for dragging a deep shortcut to the home screen.
- */
-@LargeTest
-@RunWith(AndroidJUnit4.class)
-public class ShortcutsToHomeTest extends AbstractLauncherUiTest {
-
-    @Rule public LauncherActivityRule mActivityMonitor = new LauncherActivityRule();
-    @Rule public ShellCommandRule mDefaultLauncherRule = ShellCommandRule.setDefaultLauncher();
-
-    @Test
-    public void testDragIcon_portrait() throws Throwable {
-        lockRotation(true);
-        performTest();
-    }
-
-    @Test
-    public void testDragIcon_landscape() throws Throwable {
-        lockRotation(false);
-        performTest();
-    }
-
-    private void performTest() throws Throwable {
-        clearHomescreen();
-        mActivityMonitor.startLauncher();
-
-        LauncherActivityInfo testApp  = getSettingsApp();
-
-        // Open all apps and wait for load complete.
-        final UiObject2 appsContainer = openAllApps();
-        assertTrue(Wait.atMost(Condition.minChildCount(appsContainer, 2),
-                DEFAULT_UI_TIMEOUT));
-
-        // Find the app and long press it to show shortcuts.
-        UiObject2 icon = scrollAndFind(appsContainer, By.text(testApp.getLabel().toString()));
-        // Press icon center until shortcuts appear
-        Point iconCenter = icon.getVisibleCenter();
-        sendPointer(MotionEvent.ACTION_DOWN, iconCenter);
-        UiObject2 deepShortcutsContainer = findViewById(R.id.deep_shortcuts_container);
-        assertNotNull(deepShortcutsContainer);
-        sendPointer(MotionEvent.ACTION_UP, iconCenter);
-
-        // Drag the first shortcut to the home screen.
-        assertTrue(deepShortcutsContainer.getChildCount() > 0);
-        UiObject2 shortcut = deepShortcutsContainer.getChildren().get(1)
-                .findObject(getSelectorForId(R.id.bubble_text));
-        String shortcutName = shortcut.getText();
-        dragToWorkspace(shortcut, false);
-
-        // Verify that the shortcut works on home screen
-        // (the app opens and has the same text as the shortcut).
-        mDevice.findObject(By.text(shortcutName)).click();
-        assertTrue(mDevice.wait(Until.hasObject(By.pkg(
-                testApp.getComponentName().getPackageName())
-                .text(shortcutName)), DEFAULT_UI_TIMEOUT));
-    }
-}
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
new file mode 100644
index 0000000..c3168f8
--- /dev/null
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -0,0 +1,353 @@
+/*
+ * 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.ui;
+
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.popup.ArrowPopup;
+import com.android.launcher3.tapl.AllApps;
+import com.android.launcher3.tapl.AppIcon;
+import com.android.launcher3.tapl.AppIconMenu;
+import com.android.launcher3.tapl.AppIconMenuItem;
+import com.android.launcher3.tapl.TestHelpers;
+import com.android.launcher3.tapl.Widgets;
+import com.android.launcher3.tapl.Workspace;
+import com.android.launcher3.views.OptionsPopupView;
+import com.android.launcher3.widget.WidgetsFullSheet;
+import com.android.launcher3.widget.WidgetsRecyclerView;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
+    private static final String APP_NAME = "LauncherTestApp";
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        initialize(this);
+    }
+
+    public static void initialize(AbstractLauncherUiTest test) throws Exception {
+        test.clearLauncherData();
+        test.mDevice.pressHome();
+        test.waitForLauncherCondition("Launcher didn't start", launcher -> launcher != null);
+        test.waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL);
+        test.waitForResumed("Launcher internal state is still Background");
+        // Check that we switched to home.
+        test.mLauncher.getWorkspace();
+    }
+
+    // Please don't add negative test cases for methods that fail only after a long wait.
+    public static void expectFail(String message, Runnable action) {
+        boolean failed = false;
+        try {
+            action.run();
+        } catch (AssertionError e) {
+            failed = true;
+        }
+        assertTrue(message, failed);
+    }
+
+    private boolean isWorkspaceScrollable(Launcher launcher) {
+        return launcher.getWorkspace().getPageCount() > 1;
+    }
+
+    private int getCurrentWorkspacePage(Launcher launcher) {
+        return launcher.getWorkspace().getCurrentPage();
+    }
+
+    private WidgetsRecyclerView getWidgetsView(Launcher launcher) {
+        return WidgetsFullSheet.getWidgetsView(launcher);
+    }
+
+    @Test
+    public void testDevicePressMenu() throws Exception {
+        mDevice.pressMenu();
+        mDevice.waitForIdle();
+        executeOnLauncher(
+                launcher -> assertTrue("Launcher internal state didn't switch to Showing Menu",
+                        OptionsPopupView.getOptionsPopup(launcher) != null));
+        // Check that pressHome works when the menu is shown.
+        mLauncher.pressHome();
+    }
+
+    @Test
+    @Ignore
+    public void testPressHomeOnAllAppsContextMenu() throws Exception {
+        final AllApps allApps = mLauncher.getWorkspace().switchToAllApps();
+        allApps.freeze();
+        try {
+            allApps.getAppIcon("TestActivity7").openMenu();
+        } finally {
+            allApps.unfreeze();
+        }
+        mLauncher.pressHome();
+    }
+
+    public static void runAllAppsTest(AbstractLauncherUiTest test, AllApps allApps) {
+        allApps.freeze();
+        try {
+            assertNotNull("allApps parameter is null", allApps);
+
+            assertTrue(
+                    "Launcher internal state is not All Apps",
+                    test.isInState(LauncherState.ALL_APPS));
+
+            // Test flinging forward and backward.
+            test.executeOnLauncher(launcher -> assertEquals(
+                    "All Apps started in already scrolled state", 0,
+                    test.getAllAppsScroll(launcher)));
+
+            allApps.flingForward();
+            assertTrue("Launcher internal state is not All Apps",
+                    test.isInState(LauncherState.ALL_APPS));
+            final Integer flingForwardY = test.getFromLauncher(
+                    launcher -> test.getAllAppsScroll(launcher));
+            test.executeOnLauncher(
+                    launcher -> assertTrue("flingForward() didn't scroll App Apps",
+                            flingForwardY > 0));
+
+            allApps.flingBackward();
+            assertTrue(
+                    "Launcher internal state is not All Apps",
+                    test.isInState(LauncherState.ALL_APPS));
+            final Integer flingBackwardY = test.getFromLauncher(
+                    launcher -> test.getAllAppsScroll(launcher));
+            test.executeOnLauncher(launcher -> assertTrue("flingBackward() didn't scroll App Apps",
+                    flingBackwardY < flingForwardY));
+
+            // Test scrolling down to YouTube.
+            assertNotNull("All apps: can't fine YouTube", allApps.getAppIcon("YouTube"));
+            // Test scrolling up to Camera.
+            assertNotNull("All apps: can't fine Camera", allApps.getAppIcon("Camera"));
+            // Test failing to find a non-existing app.
+            final AllApps allAppsFinal = allApps;
+            expectFail("All apps: could find a non-existing app",
+                    () -> allAppsFinal.getAppIcon("NO APP"));
+
+            assertTrue(
+                    "Launcher internal state is not All Apps",
+                    test.isInState(LauncherState.ALL_APPS));
+        } finally {
+            allApps.unfreeze();
+        }
+    }
+
+    @Test
+    @PortraitLandscape
+    public void testWorkspaceSwitchToAllApps() {
+        assertNotNull("switchToAllApps() returned null",
+                mLauncher.getWorkspace().switchToAllApps());
+        assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
+    }
+
+    @Test
+    public void testWorkspace() throws Exception {
+        final Workspace workspace = mLauncher.getWorkspace();
+
+        // Test that ensureWorkspaceIsScrollable adds a page by dragging an icon there.
+        executeOnLauncher(launcher -> assertFalse("Initial workspace state is scrollable",
+                isWorkspaceScrollable(launcher)));
+        assertNull("Chrome app was found on empty workspace",
+                workspace.tryGetWorkspaceAppIcon("Chrome"));
+
+        workspace.ensureWorkspaceIsScrollable();
+
+        executeOnLauncher(
+                launcher -> assertEquals("Ensuring workspace scrollable didn't switch to page #1",
+                        1, getCurrentWorkspacePage(launcher)));
+        executeOnLauncher(
+                launcher -> assertTrue("ensureScrollable didn't make workspace scrollable",
+                        isWorkspaceScrollable(launcher)));
+        assertNotNull("ensureScrollable didn't add Chrome app",
+                workspace.tryGetWorkspaceAppIcon("Chrome"));
+
+        // Test flinging workspace.
+        workspace.flingBackward();
+        assertTrue("Launcher internal state is not Home", isInState(LauncherState.NORMAL));
+        executeOnLauncher(
+                launcher -> assertEquals("Flinging back didn't switch workspace to page #0",
+                        0, getCurrentWorkspacePage(launcher)));
+
+        workspace.flingForward();
+        executeOnLauncher(
+                launcher -> assertEquals("Flinging forward didn't switch workspace to page #1",
+                        1, getCurrentWorkspacePage(launcher)));
+        assertTrue("Launcher internal state is not Home", isInState(LauncherState.NORMAL));
+
+        // Test starting a workspace app.
+        final AppIcon app = workspace.tryGetWorkspaceAppIcon("Chrome");
+        assertNotNull("No Chrome app in workspace", app);
+    }
+
+    public static void runIconLaunchFromAllAppsTest(AbstractLauncherUiTest test, AllApps allApps) {
+        allApps.freeze();
+        try {
+            final AppIcon app = allApps.getAppIcon("TestActivity7");
+            assertNotNull("AppIcon.launch returned null", app.launch(getAppPackageName()));
+            test.executeOnLauncher(launcher -> assertTrue(
+                    "Launcher activity is the top activity; expecting another activity to be the top "
+                            + "one",
+                    test.isInBackground(launcher)));
+        } finally {
+            allApps.unfreeze();
+        }
+    }
+
+    @Test
+    @PortraitLandscape
+    public void testAppIconLaunchFromAllAppsFromHome() throws Exception {
+        final AllApps allApps = mLauncher.getWorkspace().switchToAllApps();
+        assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
+
+        runIconLaunchFromAllAppsTest(this, allApps);
+    }
+
+    @Test
+    @PortraitLandscape
+    public void testWidgets() throws Exception {
+        // Test opening widgets.
+        executeOnLauncher(launcher ->
+                assertTrue("Widgets is initially opened", getWidgetsView(launcher) == null));
+        Widgets widgets = mLauncher.getWorkspace().openAllWidgets();
+        assertNotNull("openAllWidgets() returned null", widgets);
+        widgets = mLauncher.getAllWidgets();
+        assertNotNull("getAllWidgets() returned null", widgets);
+        executeOnLauncher(launcher ->
+                assertTrue("Widgets is not shown", getWidgetsView(launcher).isShown()));
+        executeOnLauncher(launcher -> assertEquals("Widgets is scrolled upon opening",
+                0, getWidgetsScroll(launcher)));
+
+        // Test flinging widgets.
+        widgets.flingForward();
+        Integer flingForwardY = getFromLauncher(launcher -> getWidgetsScroll(launcher));
+        executeOnLauncher(launcher -> assertTrue("Flinging forward didn't scroll widgets",
+                flingForwardY > 0));
+
+        widgets.flingBackward();
+        executeOnLauncher(launcher -> assertTrue("Flinging backward didn't scroll widgets",
+                getWidgetsScroll(launcher) < flingForwardY));
+
+        mLauncher.pressHome();
+        waitForLauncherCondition("Widgets were not closed",
+                launcher -> getWidgetsView(launcher) == null);
+    }
+
+    private int getWidgetsScroll(Launcher launcher) {
+        return getWidgetsView(launcher).getCurrentScrollY();
+    }
+
+    private boolean isOptionsPopupVisible(Launcher launcher) {
+        final ArrowPopup popup = OptionsPopupView.getOptionsPopup(launcher);
+        return popup != null && popup.isShown();
+    }
+
+    @Test
+    @PortraitLandscape
+    public void testLaunchMenuItem() throws Exception {
+        final AllApps allApps = mLauncher.
+                getWorkspace().
+                switchToAllApps();
+        allApps.freeze();
+        try {
+            final AppIconMenu menu = allApps.
+                    getAppIcon(APP_NAME).
+                    openMenu();
+
+            executeOnLauncher(
+                    launcher -> assertTrue("Launcher internal state didn't switch to Showing Menu",
+                            isOptionsPopupVisible(launcher)));
+
+            menu.getMenuItem(1).launch(getAppPackageName());
+        } finally {
+            allApps.unfreeze();
+        }
+    }
+
+    @Test
+    @PortraitLandscape
+    public void testDragAppIcon() throws Throwable {
+        // 1. Open all apps and wait for load complete.
+        // 2. Drag icon to homescreen.
+        // 3. Verify that the icon works on homescreen.
+        final AllApps allApps = mLauncher.getWorkspace().
+                switchToAllApps();
+        allApps.freeze();
+        try {
+            allApps.
+                    getAppIcon(APP_NAME).
+                    dragToWorkspace().
+                    getWorkspaceAppIcon(APP_NAME).
+                    launch(getAppPackageName());
+        } finally {
+            allApps.unfreeze();
+        }
+        executeOnLauncher(launcher -> assertTrue(
+                "Launcher activity is the top activity; expecting another activity to be the top "
+                        + "one",
+                isInBackground(launcher)));
+    }
+
+    @Test
+    @PortraitLandscape
+    public void testDragShortcut() throws Throwable {
+        // 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();
+        allApps.freeze();
+        try {
+            final AppIconMenuItem menuItem = allApps.
+                    getAppIcon(APP_NAME).
+                    openMenu().
+                    getMenuItem(0);
+            final String shortcutName = menuItem.getText();
+
+            // 4. Drag the first shortcut to the home screen.
+            // 5. Verify that the shortcut works on home screen
+            //    (the app opens and has the same text as the shortcut).
+            menuItem.
+                    dragToWorkspace().
+                    getWorkspaceAppIcon(shortcutName).
+                    launch(getAppPackageName());
+        } finally {
+            allApps.unfreeze();
+        }
+    }
+
+    public static String getAppPackageName() {
+        return getInstrumentation().getContext().getPackageName();
+    }
+}
diff --git a/tests/src/com/android/launcher3/ui/TestViewHelpers.java b/tests/src/com/android/launcher3/ui/TestViewHelpers.java
new file mode 100644
index 0000000..d13d319
--- /dev/null
+++ b/tests/src/com/android/launcher3/ui/TestViewHelpers.java
@@ -0,0 +1,187 @@
+/*
+ * 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.ui;
+
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static androidx.test.InstrumentationRegistry.getTargetContext;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.graphics.Point;
+import android.os.Process;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
+
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.R;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
+import com.android.launcher3.testcomponent.AppWidgetNoConfig;
+import com.android.launcher3.testcomponent.AppWidgetWithConfig;
+
+import java.util.concurrent.Callable;
+import java.util.function.Function;
+
+public class TestViewHelpers {
+    private static final String TAG = "TestViewHelpers";
+
+    private static UiDevice getDevice() {
+        return UiDevice.getInstance(getInstrumentation());
+    }
+
+    public static UiObject2 findViewById(int id) {
+        return getDevice().wait(Until.findObject(getSelectorForId(id)),
+                AbstractLauncherUiTest.DEFAULT_UI_TIMEOUT);
+    }
+
+    public static BySelector getSelectorForId(int id) {
+        final Context targetContext = getTargetContext();
+        String name = targetContext.getResources().getResourceEntryName(id);
+        return By.res(targetContext.getPackageName(), name);
+    }
+
+    /**
+     * Finds a widget provider which can fit on the home screen.
+     *
+     * @param test               test suite.
+     * @param hasConfigureScreen if true, a provider with a config screen is returned.
+     */
+    public static LauncherAppWidgetProviderInfo findWidgetProvider(AbstractLauncherUiTest test,
+            final boolean hasConfigureScreen) {
+        LauncherAppWidgetProviderInfo info =
+                test.getOnUiThread(new Callable<LauncherAppWidgetProviderInfo>() {
+                    @Override
+                    public LauncherAppWidgetProviderInfo call() throws Exception {
+                        ComponentName cn = new ComponentName(getInstrumentation().getContext(),
+                                hasConfigureScreen ? AppWidgetWithConfig.class
+                                        : AppWidgetNoConfig.class);
+                        Log.d(TAG, "findWidgetProvider componentName=" + cn.flattenToString());
+                        return AppWidgetManagerCompat.getInstance(getTargetContext())
+                                .findProvider(cn, Process.myUserHandle());
+                    }
+                });
+        if (info == null) {
+            throw new IllegalArgumentException("No valid widget provider");
+        }
+        return info;
+    }
+
+    /**
+     * Drags an icon to the center of homescreen.
+     *
+     * @param icon object that is either app icon or shortcut icon
+     */
+    public static void dragToWorkspace(UiObject2 icon, boolean expectedToShowShortcuts) {
+        Point center = icon.getVisibleCenter();
+
+        // Action Down
+        final long downTime = SystemClock.uptimeMillis();
+        sendPointer(downTime, MotionEvent.ACTION_DOWN, center);
+
+        UiObject2 dragLayer = findViewById(R.id.drag_layer);
+
+        if (expectedToShowShortcuts) {
+            // Make sure shortcuts show up, and then move a bit to hide them.
+            assertNotNull(findViewById(R.id.deep_shortcuts_container));
+
+            Point moveLocation = new Point(center);
+            int distanceToMove =
+                    getTargetContext().getResources().getDimensionPixelSize(
+                            R.dimen.deep_shortcuts_start_drag_threshold) + 50;
+            if (moveLocation.y - distanceToMove >= dragLayer.getVisibleBounds().top) {
+                moveLocation.y -= distanceToMove;
+            } else {
+                moveLocation.y += distanceToMove;
+            }
+            movePointer(downTime, center, moveLocation);
+
+            assertNull(findViewById(R.id.deep_shortcuts_container));
+        }
+
+        // Wait until Remove/Delete target is visible
+        assertNotNull(findViewById(R.id.delete_target_text));
+
+        Point moveLocation = dragLayer.getVisibleCenter();
+
+        // Move to center
+        movePointer(downTime, center, moveLocation);
+        sendPointer(downTime, MotionEvent.ACTION_UP, moveLocation);
+
+        // Wait until remove target is gone.
+        getDevice().wait(Until.gone(getSelectorForId(R.id.delete_target_text)),
+                AbstractLauncherUiTest.DEFAULT_UI_TIMEOUT);
+    }
+
+    private static void movePointer(long downTime, Point from, Point to) {
+        while (!from.equals(to)) {
+            try {
+                Thread.sleep(20);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+            from.x = getNextMoveValue(to.x, from.x);
+            from.y = getNextMoveValue(to.y, from.y);
+            sendPointer(downTime, MotionEvent.ACTION_MOVE, from);
+        }
+    }
+
+    private static int getNextMoveValue(int targetValue, int oldValue) {
+        if (targetValue - oldValue > 10) {
+            return oldValue + 10;
+        } else if (targetValue - oldValue < -10) {
+            return oldValue - 10;
+        } else {
+            return targetValue;
+        }
+    }
+
+    public static void sendPointer(long downTime, int action, Point point) {
+        MotionEvent event = MotionEvent.obtain(downTime,
+                SystemClock.uptimeMillis(), action, point.x, point.y, 0);
+        getInstrumentation().sendPointerSync(event);
+        event.recycle();
+    }
+
+    /**
+     * Opens widget tray and returns the recycler view.
+     */
+    public static UiObject2 openWidgetsTray() {
+        final UiDevice device = getDevice();
+        device.pressKeyCode(KeyEvent.KEYCODE_W, KeyEvent.META_CTRL_ON);
+        device.waitForIdle();
+        return findViewById(R.id.widgets_list_view);
+    }
+
+    public static View findChildView(ViewGroup parent, Function<View, Boolean> condition) {
+        for (int i = 0; i < parent.getChildCount(); ++i) {
+            final View child = parent.getChildAt(i);
+            if (condition.apply(child)) return child;
+        }
+        return null;
+    }
+}
diff --git a/tests/src/com/android/launcher3/ui/WorkTabTest.java b/tests/src/com/android/launcher3/ui/WorkTabTest.java
index 6244434..c93c20a 100644
--- a/tests/src/com/android/launcher3/ui/WorkTabTest.java
+++ b/tests/src/com/android/launcher3/ui/WorkTabTest.java
@@ -15,32 +15,21 @@
  */
 package com.android.launcher3.ui;
 
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.Until;
+import static com.android.launcher3.LauncherState.ALL_APPS;
 
-import com.android.launcher3.R;
-import com.android.launcher3.util.Condition;
-import com.android.launcher3.util.Wait;
-import com.android.launcher3.util.rule.LauncherActivityRule;
-import com.android.launcher3.util.rule.ShellCommandRule;
+import static org.junit.Assert.assertTrue;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static org.junit.Assert.assertTrue;
-
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public class WorkTabTest extends AbstractLauncherUiTest {
-    @Rule
-    public LauncherActivityRule mActivityMonitor = new LauncherActivityRule();
-    @Rule
-    public ShellCommandRule mDefaultLauncherRule = ShellCommandRule.setDefaultLauncher();
 
     private int mProfileUserId;
 
@@ -66,17 +55,13 @@
     public void workTabExists() {
         mActivityMonitor.startLauncher();
 
-        // Open all apps and wait for load complete
-        final UiObject2 appsContainer = openAllApps();
-        assertTrue(Wait.atMost(Condition.minChildCount(appsContainer, 2),
-                LARGE_UI_TIMEOUT));
+        executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
 
         /*
-        assertTrue("Personal tab is missing",
-                mDevice.wait(Until.hasObject(getSelectorForId(R.id.tab_personal)),
-                        LARGE_UI_TIMEOUT));
-        assertTrue("Work tab is missing",
-                mDevice.wait(Until.hasObject(getSelectorForId(R.id.tab_work)), LARGE_UI_TIMEOUT));
+        assertTrue("Personal tab is missing", waitForLauncherCondition(
+                launcher -> launcher.getAppsView().isPersonalTabVisible()));
+        assertTrue("Work tab is missing", waitForLauncherCondition(
+                launcher -> launcher.getAppsView().isWorkTabVisible()));
         */
     }
 }
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index a5c2e69..5eb5f19 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -15,36 +15,39 @@
  */
 package com.android.launcher3.ui.widget;
 
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+
 import android.appwidget.AppWidgetManager;
 import android.content.Intent;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.UiObject2;
 import android.view.View;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiObject2;
+
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.testcomponent.WidgetConfigActivity;
 import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.ui.TestViewHelpers;
 import com.android.launcher3.util.Condition;
 import com.android.launcher3.util.Wait;
-import com.android.launcher3.util.rule.LauncherActivityRule;
 import com.android.launcher3.util.rule.ShellCommandRule;
 import com.android.launcher3.widget.WidgetCell;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNotSame;
-import static org.junit.Assert.assertTrue;
-
 /**
  * Test to verify widget configuration is properly shown.
  */
@@ -52,8 +55,7 @@
 @RunWith(AndroidJUnit4.class)
 public class AddConfigWidgetTest extends AbstractLauncherUiTest {
 
-    @Rule public LauncherActivityRule mActivityMonitor = new LauncherActivityRule();
-    @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grandWidgetBind();
+    @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
 
     private LauncherAppWidgetProviderInfo mWidgetInfo;
     private AppWidgetManager mAppWidgetManager;
@@ -64,26 +66,30 @@
     @Before
     public void setUp() throws Exception {
         super.setUp();
-        mWidgetInfo = findWidgetProvider(true /* hasConfigureScreen */);
+        mWidgetInfo = TestViewHelpers.findWidgetProvider(this, true /* hasConfigureScreen */);
         mAppWidgetManager = AppWidgetManager.getInstance(mTargetContext);
     }
 
     @Test
+    // Convert test to TAPL b/131116002
     public void testWidgetConfig() throws Throwable {
         runTest(false, true);
     }
 
     @Test
+    @Ignore // b/121280703
     public void testWidgetConfig_rotate() throws Throwable {
         runTest(true, true);
     }
 
     @Test
+    // Convert test to TAPL b/131116002
     public void testConfigCancelled() throws Throwable {
         runTest(false, false);
     }
 
     @Test
+    @Ignore // b/121280703
     public void testConfigCancelled_rotate() throws Throwable {
         runTest(true, false);
     }
@@ -99,14 +105,14 @@
         mActivityMonitor.startLauncher();
 
         // Open widget tray and wait for load complete.
-        final UiObject2 widgetContainer = openWidgetsTray();
-        assertTrue(Wait.atMost(Condition.minChildCount(widgetContainer, 2), DEFAULT_UI_TIMEOUT));
+        final UiObject2 widgetContainer = TestViewHelpers.openWidgetsTray();
+        Wait.atMost(null, Condition.minChildCount(widgetContainer, 2), DEFAULT_UI_TIMEOUT);
 
         // Drag widget to homescreen
         WidgetConfigStartupMonitor monitor = new WidgetConfigStartupMonitor();
         UiObject2 widget = scrollAndFind(widgetContainer, By.clazz(WidgetCell.class)
                 .hasDescendant(By.text(mWidgetInfo.getLabel(mTargetContext.getPackageManager()))));
-        dragToWorkspace(widget, false);
+        TestViewHelpers.dragToWorkspace(widget, false);
         // Widget id for which the config activity was opened
         mWidgetId = monitor.getWidgetId();
 
@@ -122,21 +128,16 @@
 
         setResult(acceptConfig);
         if (acceptConfig) {
-            assertTrue(Wait.atMost(new WidgetSearchCondition(), DEFAULT_ACTIVITY_TIMEOUT));
+            Wait.atMost(null, new WidgetSearchCondition(), DEFAULT_ACTIVITY_TIMEOUT);
             assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId));
         } else {
             // Verify that the widget id is deleted.
-            assertTrue(Wait.atMost(new Condition() {
-                @Override
-                public boolean isTrue() throws Throwable {
-                    return mAppWidgetManager.getAppWidgetInfo(mWidgetId) == null;
-                }
-            }, DEFAULT_ACTIVITY_TIMEOUT));
+            Wait.atMost(null, () -> mAppWidgetManager.getAppWidgetInfo(mWidgetId) == null,
+                    DEFAULT_ACTIVITY_TIMEOUT);
         }
     }
 
     private void setResult(boolean success) {
-
         getInstrumentation().getTargetContext().sendBroadcast(
                 WidgetConfigActivity.getCommandIntent(WidgetConfigActivity.class,
                         success ? "clickOK" : "clickCancel"));
@@ -145,8 +146,7 @@
     /**
      * Condition for searching widget id
      */
-    private class WidgetSearchCondition extends Condition
-            implements Workspace.ItemOperator {
+    private class WidgetSearchCondition implements Condition, Workspace.ItemOperator {
 
         @Override
         public boolean isTrue() throws Throwable {
@@ -156,7 +156,8 @@
         @Override
         public boolean evaluate(ItemInfo info, View view) {
             return info instanceof LauncherAppWidgetInfo &&
-                    ((LauncherAppWidgetInfo) info).providerName.equals(mWidgetInfo.provider) &&
+                    ((LauncherAppWidgetInfo) info).providerName.getClassName().equals(
+                            mWidgetInfo.provider.getClassName()) &&
                     ((LauncherAppWidgetInfo) info).appWidgetId == mWidgetId;
         }
     }
diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
index 19f7db7..0061568 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
@@ -15,10 +15,12 @@
  */
 package com.android.launcher3.ui.widget;
 
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.UiObject2;
+import static org.junit.Assert.assertTrue;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiObject2;
 import android.view.View;
 
 import com.android.launcher3.ItemInfo;
@@ -26,18 +28,17 @@
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.Workspace.ItemOperator;
 import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.ui.TestViewHelpers;
 import com.android.launcher3.util.Condition;
 import com.android.launcher3.util.Wait;
-import com.android.launcher3.util.rule.LauncherActivityRule;
 import com.android.launcher3.util.rule.ShellCommandRule;
 import com.android.launcher3.widget.WidgetCell;
 
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static org.junit.Assert.assertTrue;
-
 /**
  * Test to add widget from widget tray
  */
@@ -45,8 +46,7 @@
 @RunWith(AndroidJUnit4.class)
 public class AddWidgetTest extends AbstractLauncherUiTest {
 
-    @Rule public LauncherActivityRule mActivityMonitor = new LauncherActivityRule();
-    @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grandWidgetBind();
+    @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
 
     @Test
     public void testDragIcon_portrait() throws Throwable {
@@ -55,32 +55,35 @@
     }
 
     @Test
+    @Ignore // b/121280703
     public void testDragIcon_landscape() throws Throwable {
         lockRotation(false);
         performTest();
     }
 
+    // Convert to TAPL b/131116002
     private void performTest() throws Throwable {
         clearHomescreen();
         mActivityMonitor.startLauncher();
 
         final LauncherAppWidgetProviderInfo widgetInfo =
-                findWidgetProvider(false /* hasConfigureScreen */);
+                TestViewHelpers.findWidgetProvider(this, false /* hasConfigureScreen */);
 
         // Open widget tray and wait for load complete.
-        final UiObject2 widgetContainer = openWidgetsTray();
-        assertTrue(Wait.atMost(Condition.minChildCount(widgetContainer, 2), DEFAULT_UI_TIMEOUT));
+        final UiObject2 widgetContainer = TestViewHelpers.openWidgetsTray();
+        Wait.atMost(null, Condition.minChildCount(widgetContainer, 2), DEFAULT_UI_TIMEOUT);
 
         // Drag widget to homescreen
         UiObject2 widget = scrollAndFind(widgetContainer, By.clazz(WidgetCell.class)
                 .hasDescendant(By.text(widgetInfo.getLabel(mTargetContext.getPackageManager()))));
-        dragToWorkspace(widget, false);
+        TestViewHelpers.dragToWorkspace(widget, false);
 
         assertTrue(mActivityMonitor.itemExists(new ItemOperator() {
             @Override
             public boolean evaluate(ItemInfo info, View view) {
                 return info instanceof LauncherAppWidgetInfo &&
-                        ((LauncherAppWidgetInfo) info).providerName.equals(widgetInfo.provider);
+                        ((LauncherAppWidgetInfo) info).providerName.getClassName().equals(
+                                widgetInfo.provider.getClassName());
             }
         }).call());
     }
diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index b557119..d36126b 100644
--- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
@@ -15,53 +15,46 @@
  */
 package com.android.launcher3.ui.widget;
 
+import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
 import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetManager;
 import android.content.ComponentName;
 import android.content.ContentResolver;
-import android.content.ContentValues;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageInstaller.SessionParams;
 import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.os.Bundle;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.test.uiautomator.UiSelector;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.android.launcher3.LauncherAppWidgetHost;
-import com.android.launcher3.widget.LauncherAppWidgetHostView;
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.widget.PendingAppWidgetHostView;
-import com.android.launcher3.Workspace;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.PackageInstallerCompat;
+import com.android.launcher3.tapl.Workspace;
 import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.ui.TestViewHelpers;
 import com.android.launcher3.util.ContentWriter;
-import com.android.launcher3.util.LooperExecutor;
-import com.android.launcher3.util.rule.LauncherActivityRule;
 import com.android.launcher3.util.rule.ShellCommandRule;
 import com.android.launcher3.widget.PendingAddWidgetInfo;
 import com.android.launcher3.widget.WidgetHostViewLoader;
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.TimeUnit;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
 
 /**
  * Tests for bind widget flow.
@@ -72,8 +65,8 @@
 @RunWith(AndroidJUnit4.class)
 public class BindWidgetTest extends AbstractLauncherUiTest {
 
-    @Rule public LauncherActivityRule mActivityMonitor = new LauncherActivityRule();
-    @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grandWidgetBind();
+    @Rule
+    public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
 
     private ContentResolver mResolver;
     private AppWidgetManagerCompat mWidgetManager;
@@ -97,7 +90,7 @@
     }
 
     @After
-    public void tearDown() throws Exception {
+    public void tearDown() {
         if (mCursor != null) {
             mCursor.close();
         }
@@ -109,62 +102,65 @@
 
     @Test
     public void testBindNormalWidget_withConfig() {
-        LauncherAppWidgetProviderInfo info = findWidgetProvider(true);
+        LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, true);
         LauncherAppWidgetInfo item = createWidgetInfo(info, true);
 
-        setupAndVerifyContents(item, LauncherAppWidgetHostView.class, info.label);
+        setupContents(item);
+        verifyWidgetPresent(info);
     }
 
     @Test
     public void testBindNormalWidget_withoutConfig() {
-        LauncherAppWidgetProviderInfo info = findWidgetProvider(false);
+        LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
         LauncherAppWidgetInfo item = createWidgetInfo(info, true);
 
-        setupAndVerifyContents(item, LauncherAppWidgetHostView.class, info.label);
+        setupContents(item);
+        verifyWidgetPresent(info);
     }
 
-    @Test @Ignore
-    public void testUnboundWidget_removed() throws Exception {
-        LauncherAppWidgetProviderInfo info = findWidgetProvider(false);
+    @Test
+    public void testUnboundWidget_removed() {
+        LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
         LauncherAppWidgetInfo item = createWidgetInfo(info, false);
         item.appWidgetId = -33;
 
-        // Since there is no widget to verify, just wait until the workspace is ready.
-        setupAndVerifyContents(item, Workspace.class, null);
+        setupContents(item);
 
-        waitUntilLoaderIdle();
+        final Workspace workspace = mLauncher.getWorkspace();
         // Item deleted from db
         mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id),
                 null, null, null, null, null);
         assertEquals(0, mCursor.getCount());
 
         // The view does not exist
-        assertFalse(mDevice.findObject(new UiSelector().description(info.label)).exists());
+        assertTrue("Widget exists", workspace.tryGetWidget(info.label, 0) == null);
     }
 
     @Test
     public void testPendingWidget_autoRestored() {
         // A non-restored widget with no config screen gets restored automatically.
-        LauncherAppWidgetProviderInfo info = findWidgetProvider(false);
+        LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
 
         // Do not bind the widget
         LauncherAppWidgetInfo item = createWidgetInfo(info, false);
         item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID;
 
-        setupAndVerifyContents(item, LauncherAppWidgetHostView.class, info.label);
+        setupContents(item);
+        verifyWidgetPresent(info);
     }
 
     @Test
-    public void testPendingWidget_withConfigScreen() throws Exception {
+    public void testPendingWidget_withConfigScreen() {
         // A non-restored widget with config screen get bound and shows a 'Click to setup' UI.
-        LauncherAppWidgetProviderInfo info = findWidgetProvider(true);
+        LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, true);
 
         // Do not bind the widget
         LauncherAppWidgetInfo item = createWidgetInfo(info, false);
         item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID;
 
-        setupAndVerifyContents(item, PendingAppWidgetHostView.class, null);
-        waitUntilLoaderIdle();
+        setupContents(item);
+        verifyPendingWidgetPresent();
+
         // Item deleted from db
         mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id),
                 null, null, null, null, null);
@@ -178,17 +174,16 @@
                         LauncherSettings.Favorites.APPWIDGET_ID))));
     }
 
-    @Test @Ignore
-    public void testPendingWidget_notRestored_removed() throws Exception {
+    @Test
+    public void testPendingWidget_notRestored_removed() {
         LauncherAppWidgetInfo item = getInvalidWidgetInfo();
         item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID
                 | LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
 
-        setupAndVerifyContents(item, Workspace.class, null);
-        // The view does not exist
-        assertFalse(mDevice.findObject(
-                new UiSelector().className(PendingAppWidgetHostView.class)).exists());
-        waitUntilLoaderIdle();
+        setupContents(item);
+
+        assertTrue("Pending widget exists",
+                mLauncher.getWorkspace().tryGetPendingWidget(0) == null);
         // Item deleted from db
         mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id),
                 null, null, null, null, null);
@@ -196,7 +191,7 @@
     }
 
     @Test
-    public void testPendingWidget_notRestored_brokenInstall() throws Exception {
+    public void testPendingWidget_notRestored_brokenInstall() {
         // A widget which is was being installed once, even if its not being
         // installed at the moment is not removed.
         LauncherAppWidgetInfo item = getInvalidWidgetInfo();
@@ -204,9 +199,10 @@
                 | LauncherAppWidgetInfo.FLAG_RESTORE_STARTED
                 | LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
 
-        setupAndVerifyContents(item, PendingAppWidgetHostView.class, null);
+        setupContents(item);
+        verifyPendingWidgetPresent();
+
         // Verify item still exists in db
-        waitUntilLoaderIdle();
         mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id),
                 null, null, null, null, null);
         assertEquals(1, mCursor.getCount());
@@ -231,9 +227,10 @@
         PackageInstaller installer = mTargetContext.getPackageManager().getPackageInstaller();
         mSessionId = installer.createSession(params);
 
-        setupAndVerifyContents(item, PendingAppWidgetHostView.class, null);
+        setupContents(item);
+        verifyPendingWidgetPresent();
+
         // Verify item still exists in db
-        waitUntilLoaderIdle();
         mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id),
                 null, null, null, null, null);
         assertEquals(1, mCursor.getCount());
@@ -248,28 +245,21 @@
     /**
      * Adds {@param item} on the homescreen on the 0th screen at 0,0, and verifies that the
      * widget class is displayed on the homescreen.
-     * @param widgetClass the View class which is displayed on the homescreen
-     * @param desc the content description of the view or null.
      */
-    private void setupAndVerifyContents(
-            LauncherAppWidgetInfo item, Class<?> widgetClass, String desc) {
-        long screenId = Workspace.FIRST_SCREEN_ID;
+    private void setupContents(LauncherAppWidgetInfo item) {
+        int screenId = FIRST_SCREEN_ID;
         // Update the screen id counter for the provider.
         LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_NEW_SCREEN_ID);
 
-        if (screenId > Workspace.FIRST_SCREEN_ID) {
-            screenId = Workspace.FIRST_SCREEN_ID;
+        if (screenId > FIRST_SCREEN_ID) {
+            screenId = FIRST_SCREEN_ID;
         }
-        ContentValues v = new ContentValues();
-        v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
-        v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, 0);
-        mResolver.insert(LauncherSettings.WorkspaceScreens.CONTENT_URI, v);
 
         // Insert the item
         ContentWriter writer = new ContentWriter(mTargetContext);
         item.id = LauncherSettings.Settings.call(
                 mResolver, LauncherSettings.Settings.METHOD_NEW_ITEM_ID)
-                .getLong(LauncherSettings.Settings.EXTRA_VALUE);
+                .getInt(LauncherSettings.Settings.EXTRA_VALUE);
         item.screenId = screenId;
         item.onAddToDatabase(writer);
         writer.put(LauncherSettings.Favorites._ID, item.id);
@@ -278,13 +268,17 @@
 
         // Launch the home activity
         mActivityMonitor.startLauncher();
-        // Verify UI
-        UiSelector selector = new UiSelector().packageName(mTargetContext.getPackageName())
-                .className(widgetClass);
-        if (desc != null) {
-            selector = selector.description(desc);
-        }
-        assertTrue(mDevice.findObject(selector).waitForExists(DEFAULT_UI_TIMEOUT));
+        waitForModelLoaded();
+    }
+
+    private void verifyWidgetPresent(LauncherAppWidgetProviderInfo info) {
+        assertTrue("Widget is not present",
+                mLauncher.getWorkspace().tryGetWidget(info.label, DEFAULT_UI_TIMEOUT) != null);
+    }
+
+    private void verifyPendingWidgetPresent() {
+        assertTrue("Pending widget is not present",
+                mLauncher.getWorkspace().tryGetPendingWidget(DEFAULT_UI_TIMEOUT) != null);
     }
 
     /**
@@ -332,13 +326,9 @@
         int count = 0;
         String pkg = invalidPackage;
 
-        Set<String> activePackage = getOnUiThread(new Callable<Set<String>>() {
-            @Override
-            public Set<String> call() throws Exception {
-                return PackageInstallerCompat.getInstance(mTargetContext)
-                        .updateAndGetActiveSessionCache().keySet();
-            }
-        });
+        Set<String> activePackage = getOnUiThread(() ->
+                PackageInstallerCompat.getInstance(mTargetContext)
+                        .updateAndGetActiveSessionCache().keySet());
         while(true) {
             try {
                 mTargetContext.getPackageManager().getPackageInfo(
@@ -362,15 +352,4 @@
         item.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
         return item;
     }
-
-    /**
-     * Blocks the current thread until all the jobs in the main worker thread are complete.
-     */
-    private void waitUntilLoaderIdle() throws Exception {
-        new LooperExecutor(LauncherModel.getWorkerLooper())
-                .submit(new Runnable() {
-                    @Override
-                    public void run() { }
-                }).get(DEFAULT_WORKER_TIMEOUT_SECS, TimeUnit.SECONDS);
-    }
 }
diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
index dcb564a..6122dae 100644
--- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
@@ -15,57 +15,51 @@
  */
 package com.android.launcher3.ui.widget;
 
+import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+
 import android.app.PendingIntent;
 import android.appwidget.AppWidgetManager;
 import android.content.Intent;
 import android.graphics.Color;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.Until;
 import android.view.View;
 
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.R;
-import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace.ItemOperator;
+import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.tapl.AddToHomeScreenPrompt;
 import com.android.launcher3.testcomponent.AppWidgetNoConfig;
 import com.android.launcher3.testcomponent.AppWidgetWithConfig;
 import com.android.launcher3.testcomponent.RequestPinItemActivity;
 import com.android.launcher3.ui.AbstractLauncherUiTest;
 import com.android.launcher3.util.Condition;
 import com.android.launcher3.util.Wait;
-import com.android.launcher3.util.rule.LauncherActivityRule;
 import com.android.launcher3.util.rule.ShellCommandRule;
-import com.android.launcher3.widget.WidgetCell;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.util.UUID;
 
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNotSame;
-import static org.junit.Assert.assertTrue;
-
 /**
  * Test to verify pin item request flow.
  */
 @LargeTest
 @RunWith(AndroidJUnit4.class)
-public class RequestPinItemTest  extends AbstractLauncherUiTest {
+public class RequestPinItemTest extends AbstractLauncherUiTest {
 
-    @Rule public LauncherActivityRule mActivityMonitor = new LauncherActivityRule();
-    @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grandWidgetBind();
-    @Rule public ShellCommandRule mDefaultLauncherRule = ShellCommandRule.setDefaultLauncher();
+    @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
 
     private String mCallbackAction;
     private String mShortcutId;
@@ -82,51 +76,37 @@
     @Test
     public void testEmpty() throws Throwable { /* needed while the broken tests are being fixed */ }
 
-    @Test @Ignore
+    @Test
     public void testPinWidgetNoConfig() throws Throwable {
-        runTest("pinWidgetNoConfig", true, new ItemOperator() {
-            @Override
-            public boolean evaluate(ItemInfo info, View view) {
-                return info instanceof LauncherAppWidgetInfo &&
-                        ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId &&
-                        ((LauncherAppWidgetInfo) info).providerName.getClassName()
-                                .equals(AppWidgetNoConfig.class.getName());
-            }
-        });
+        runTest("pinWidgetNoConfig", true, (info, view) -> info instanceof LauncherAppWidgetInfo &&
+                ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId &&
+                ((LauncherAppWidgetInfo) info).providerName.getClassName()
+                        .equals(AppWidgetNoConfig.class.getName()));
     }
 
-    @Test @Ignore
+        @Test
     public void testPinWidgetNoConfig_customPreview() throws Throwable {
         // Command to set custom preview
         Intent command =  RequestPinItemActivity.getCommandIntent(
                 RequestPinItemActivity.class, "setRemoteViewColor").putExtra(
                 RequestPinItemActivity.EXTRA_PARAM + "0", Color.RED);
 
-        runTest("pinWidgetNoConfig", true, new ItemOperator() {
-            @Override
-            public boolean evaluate(ItemInfo info, View view) {
-                return info instanceof LauncherAppWidgetInfo &&
-                        ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId &&
-                        ((LauncherAppWidgetInfo) info).providerName.getClassName()
-                                .equals(AppWidgetNoConfig.class.getName());
-            }
-        }, command);
+        runTest("pinWidgetNoConfig", true, (info, view) -> info instanceof LauncherAppWidgetInfo &&
+                ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId &&
+                ((LauncherAppWidgetInfo) info).providerName.getClassName()
+                        .equals(AppWidgetNoConfig.class.getName()), command);
     }
 
-    @Test @Ignore
+    @Test
     public void testPinWidgetWithConfig() throws Throwable {
-        runTest("pinWidgetWithConfig", true, new ItemOperator() {
-            @Override
-            public boolean evaluate(ItemInfo info, View view) {
-                return info instanceof LauncherAppWidgetInfo &&
+        runTest("pinWidgetWithConfig", true,
+                (info, view) -> info instanceof LauncherAppWidgetInfo &&
                         ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId &&
                         ((LauncherAppWidgetInfo) info).providerName.getClassName()
-                                .equals(AppWidgetWithConfig.class.getName());
-            }
-        });
+                                .equals(AppWidgetWithConfig.class.getName()));
     }
 
-    @Test @Ignore
+    @Test
     public void testPinShortcut() throws Throwable {
         // Command to set the shortcut id
         Intent command = RequestPinItemActivity.getCommandIntent(
@@ -136,7 +116,7 @@
         runTest("pinShortcut", false, new ItemOperator() {
             @Override
             public boolean evaluate(ItemInfo info, View view) {
-                return info instanceof ShortcutInfo &&
+                return info instanceof WorkspaceItemInfo &&
                         info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT &&
                         ShortcutKey.fromItemInfo(info).getId().equals(mShortcutId);
             }
@@ -153,14 +133,14 @@
         clearHomescreen();
         mActivityMonitor.startLauncher();
 
-        // Open all apps and wait for load complete
-        final UiObject2 appsContainer = openAllApps();
-        assertTrue(Wait.atMost(Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT));
-
         // Open Pin item activity
         BlockingBroadcastReceiver openMonitor = new BlockingBroadcastReceiver(
                 RequestPinItemActivity.class.getName());
-        scrollAndFind(appsContainer, By.text("Test Pin Item")).click();
+        mLauncher.
+                getWorkspace().
+                switchToAllApps().
+                getAppIcon("Test Pin Item").
+                launch(getAppPackageName());
         assertNotNull(openMonitor.blockingGetExtraIntent());
 
         // Set callback
@@ -177,14 +157,11 @@
         // call the requested method to start the flow
         mTargetContext.sendBroadcast(RequestPinItemActivity.getCommandIntent(
                 RequestPinItemActivity.class, activityMethod));
-        UiObject2 widgetCell = mDevice.wait(
-                Until.findObject(By.clazz(WidgetCell.class)), DEFAULT_ACTIVITY_TIMEOUT);
-        assertNotNull(widgetCell);
+        final AddToHomeScreenPrompt addToHomeScreenPrompt = mLauncher.getAddToHomeScreenPrompt();
 
         // Accept confirmation:
         BlockingBroadcastReceiver resultReceiver = new BlockingBroadcastReceiver(mCallbackAction);
-        mDevice.wait(Until.findObject(By.text(mTargetContext.getString(
-                R.string.place_automatically).toUpperCase())), DEFAULT_UI_TIMEOUT).click();
+        addToHomeScreenPrompt.addAutomatically();
         Intent result = resultReceiver.blockingGetIntent();
         assertNotNull(result);
         mAppWidgetId = result.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
@@ -193,14 +170,14 @@
         }
 
         // Go back to home
-        mActivityMonitor.returnToHome();
-        assertTrue(Wait.atMost(new ItemSearchCondition(itemMatcher), DEFAULT_ACTIVITY_TIMEOUT));
+        mLauncher.pressHome();
+        Wait.atMost(null, new ItemSearchCondition(itemMatcher), DEFAULT_ACTIVITY_TIMEOUT);
     }
 
     /**
      * Condition for for an item
      */
-    private class ItemSearchCondition extends Condition {
+    private class ItemSearchCondition implements Condition {
 
         private final ItemOperator mOp;
 
diff --git a/tests/src/com/android/launcher3/util/Condition.java b/tests/src/com/android/launcher3/util/Condition.java
index e9ee67c..b564a1a 100644
--- a/tests/src/com/android/launcher3/util/Condition.java
+++ b/tests/src/com/android/launcher3/util/Condition.java
@@ -1,6 +1,6 @@
 package com.android.launcher3.util;
 
-import android.support.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.UiObject2;
 
 import com.android.launcher3.MainThreadExecutor;
 
@@ -8,47 +8,36 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-public abstract class Condition {
+public interface Condition {
 
-    public abstract boolean isTrue() throws Throwable;
+    boolean isTrue() throws Throwable;
 
     /**
      * Converts the condition to be run on UI thread.
      */
-    public static Condition runOnUiThread(final Condition condition) {
+    static Condition runOnUiThread(final Condition condition) {
         final MainThreadExecutor executor = new MainThreadExecutor();
-        return new Condition() {
-            @Override
-            public boolean isTrue() throws Throwable {
-                final AtomicBoolean value = new AtomicBoolean(false);
-                final Throwable[] exceptions = new Throwable[1];
-                final CountDownLatch latch = new CountDownLatch(1);
-                executor.execute(new Runnable() {
-                    @Override
-                    public void run() {
-                        try {
-                            value.set(condition.isTrue());
-                        } catch (Throwable e) {
-                            exceptions[0] = e;
-                        }
-
-                    }
-                });
-                latch.await(1, TimeUnit.SECONDS);
-                if (exceptions[0] != null) {
-                    throw exceptions[0];
+        return () -> {
+            final AtomicBoolean value = new AtomicBoolean(false);
+            final Throwable[] exceptions = new Throwable[1];
+            final CountDownLatch latch = new CountDownLatch(1);
+            executor.execute(() -> {
+                try {
+                    value.set(condition.isTrue());
+                } catch (Throwable e) {
+                    exceptions[0] = e;
                 }
-                return value.get();
+
+            });
+            latch.await(1, TimeUnit.SECONDS);
+            if (exceptions[0] != null) {
+                throw exceptions[0];
             }
+            return value.get();
         };
     }
 
-    public static Condition minChildCount(final UiObject2 obj, final int childCount) {
-        return new Condition() {
-            @Override
-            public boolean isTrue() {
-                return obj.getChildCount() >= childCount;
-            }
-        };
+    static Condition minChildCount(final UiObject2 obj, final int childCount) {
+        return () -> obj.getChildCount() >= childCount;
     }
 }
diff --git a/tests/src/com/android/launcher3/util/GridOccupancyTest.java b/tests/src/com/android/launcher3/util/GridOccupancyTest.java
deleted file mode 100644
index fdd8e88..0000000
--- a/tests/src/com/android/launcher3/util/GridOccupancyTest.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package com.android.launcher3.util;
-
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-/**
- * Unit tests for {@link GridOccupancy}
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class GridOccupancyTest {
-
-    @Test
-    public void testFindVacantCell() {
-        GridOccupancy grid = initGrid(4,
-                1, 1, 1, 0, 0,
-                0, 0, 1, 1, 0,
-                0, 0, 0, 0, 0,
-                1, 1, 0, 0, 0
-                );
-
-        int[] vacant = new int[2];
-        assertTrue(grid.findVacantCell(vacant, 2, 2));
-        assertEquals(vacant[0], 0);
-        assertEquals(vacant[1], 1);
-
-        assertTrue(grid.findVacantCell(vacant, 3, 2));
-        assertEquals(vacant[0], 2);
-        assertEquals(vacant[1], 2);
-
-        assertFalse(grid.findVacantCell(vacant, 3, 3));
-    }
-
-    @Test
-    public void testIsRegionVacant() {
-        GridOccupancy grid = initGrid(4,
-                1, 1, 1, 0, 0,
-                0, 0, 1, 1, 0,
-                0, 0, 0, 0, 0,
-                1, 1, 0, 0, 0
-        );
-
-        assertTrue(grid.isRegionVacant(4, 0, 1, 4));
-        assertTrue(grid.isRegionVacant(0, 1, 2, 2));
-        assertTrue(grid.isRegionVacant(2, 2, 3, 2));
-
-        assertFalse(grid.isRegionVacant(3, 0, 2, 4));
-        assertFalse(grid.isRegionVacant(0, 0, 2, 1));
-    }
-
-    private GridOccupancy initGrid(int rows, int... cells) {
-        int cols = cells.length / rows;
-        int i = 0;
-        GridOccupancy grid = new GridOccupancy(cols, rows);
-        for (int y = 0; y < rows; y++) {
-            for (int x = 0; x < cols; x++) {
-                grid.cells[x][y] = cells[i] != 0;
-                i++;
-            }
-        }
-        return grid;
-    }
-}
diff --git a/tests/src/com/android/launcher3/util/LauncherLayoutBuilder.java b/tests/src/com/android/launcher3/util/LauncherLayoutBuilder.java
new file mode 100644
index 0000000..d3659eb
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/LauncherLayoutBuilder.java
@@ -0,0 +1,172 @@
+/**
+ * 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 android.text.TextUtils;
+import android.util.Pair;
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Helper class to build xml for Launcher Layout
+ */
+public class LauncherLayoutBuilder {
+
+    // Object Tags
+    private static final String TAG_WORKSPACE = "workspace";
+    private static final String TAG_AUTO_INSTALL = "autoinstall";
+    private static final String TAG_FOLDER = "folder";
+    private static final String TAG_APPWIDGET = "appwidget";
+    private static final String TAG_EXTRA = "extra";
+
+    private static final String ATTR_CONTAINER = "container";
+    private static final String ATTR_RANK = "rank";
+
+    private static final String ATTR_PACKAGE_NAME = "packageName";
+    private static final String ATTR_CLASS_NAME = "className";
+    private static final String ATTR_TITLE = "title";
+    private static final String ATTR_SCREEN = "screen";
+
+    // x and y can be specified as negative integers, in which case -1 represents the
+    // last row / column, -2 represents the second last, and so on.
+    private static final String ATTR_X = "x";
+    private static final String ATTR_Y = "y";
+    private static final String ATTR_SPAN_X = "spanX";
+    private static final String ATTR_SPAN_Y = "spanY";
+
+    private static final String ATTR_CHILDREN = "children";
+
+
+    // Style attrs -- "Extra"
+    private static final String ATTR_KEY = "key";
+    private static final String ATTR_VALUE = "value";
+
+    private static final String CONTAINER_DESKTOP = "desktop";
+    private static final String CONTAINER_HOTSEAT = "hotseat";
+
+    private final ArrayList<Pair<String, HashMap<String, Object>>> mNodes = new ArrayList<>();
+
+    public Location atHotseat(int rank) {
+        Location l = new Location();
+        l.items.put(ATTR_CONTAINER, CONTAINER_HOTSEAT);
+        l.items.put(ATTR_RANK, Integer.toString(rank));
+        return l;
+    }
+
+    public Location atWorkspace(int x, int y, int screen) {
+        Location l = new Location();
+        l.items.put(ATTR_CONTAINER, CONTAINER_DESKTOP);
+        l.items.put(ATTR_X, Integer.toString(x));
+        l.items.put(ATTR_Y, Integer.toString(y));
+        l.items.put(ATTR_SCREEN, Integer.toString(screen));
+        return l;
+    }
+
+    public String build() throws IOException {
+        StringWriter writer = new StringWriter();
+        build(writer);
+        return writer.toString();
+    }
+
+    public void build(Writer writer) throws IOException {
+        XmlSerializer serializer = Xml.newSerializer();
+        serializer.setOutput(writer);
+
+        serializer.startDocument("UTF-8", true);
+        serializer.startTag(null, TAG_WORKSPACE);
+        writeNodes(serializer, mNodes);
+        serializer.endTag(null, TAG_WORKSPACE);
+        serializer.endDocument();
+        serializer.flush();
+    }
+
+    private static void writeNodes(XmlSerializer serializer,
+            ArrayList<Pair<String, HashMap<String, Object>>> nodes) throws IOException {
+        for (Pair<String, HashMap<String, Object>> node : nodes) {
+            ArrayList<Pair<String, HashMap<String, Object>>> children = null;
+
+            serializer.startTag(null, node.first);
+            for (Map.Entry<String, Object> attr : node.second.entrySet()) {
+                if (ATTR_CHILDREN.equals(attr.getKey())) {
+                    children = (ArrayList<Pair<String, HashMap<String, Object>>>) attr.getValue();
+                } else {
+                    serializer.attribute(null, attr.getKey(), (String) attr.getValue());
+                }
+            }
+
+            if (children != null) {
+                writeNodes(serializer, children);
+            }
+            serializer.endTag(null, node.first);
+        }
+    }
+
+    public class Location {
+
+        final HashMap<String, Object> items = new HashMap<>();
+
+        public LauncherLayoutBuilder putApp(String packageName, String className) {
+            items.put(ATTR_PACKAGE_NAME, packageName);
+            items.put(ATTR_CLASS_NAME, TextUtils.isEmpty(className) ? packageName : className);
+            mNodes.add(Pair.create(TAG_AUTO_INSTALL, items));
+            return LauncherLayoutBuilder.this;
+        }
+
+        public LauncherLayoutBuilder putWidget(String packageName, String className,
+                int spanX, int spanY) {
+            items.put(ATTR_PACKAGE_NAME, packageName);
+            items.put(ATTR_CLASS_NAME, className);
+            items.put(ATTR_SPAN_X, Integer.toString(spanX));
+            items.put(ATTR_SPAN_Y, Integer.toString(spanY));
+            mNodes.add(Pair.create(TAG_APPWIDGET, items));
+            return LauncherLayoutBuilder.this;
+        }
+
+        public FolderBuilder putFolder(int titleResId) {
+            FolderBuilder folderBuilder = new FolderBuilder();
+            items.put(ATTR_TITLE, Integer.toString(titleResId));
+            items.put(ATTR_CHILDREN, folderBuilder.mChildren);
+            mNodes.add(Pair.create(TAG_FOLDER, items));
+            return folderBuilder;
+        }
+    }
+
+    public class FolderBuilder {
+
+        final ArrayList<Pair<String, HashMap<String, Object>>> mChildren = new ArrayList<>();
+
+        public FolderBuilder addApp(String packageName, String className) {
+            HashMap<String, Object> items = new HashMap<>();
+            items.put(ATTR_PACKAGE_NAME, packageName);
+            items.put(ATTR_CLASS_NAME, TextUtils.isEmpty(className) ? packageName : className);
+            mChildren.add(Pair.create(TAG_AUTO_INSTALL, items));
+            return this;
+        }
+
+        public LauncherLayoutBuilder build() {
+            return LauncherLayoutBuilder.this;
+        }
+    }
+}
diff --git a/tests/src/com/android/launcher3/util/RaceConditionReproducer.java b/tests/src/com/android/launcher3/util/RaceConditionReproducer.java
new file mode 100644
index 0000000..0235f95
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/RaceConditionReproducer.java
@@ -0,0 +1,488 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+import static com.android.launcher3.util.RaceConditionTracker.ENTER_POSTFIX;
+import static com.android.launcher3.util.RaceConditionTracker.EXIT_POSTFIX;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Event processor for reliably reproducing multithreaded apps race conditions in tests.
+ *
+ * The app notifies us about “events” that happen in its threads. The race condition test runs the
+ * test action multiple times (aka iterations), trying to generate all possible permutations of
+ * these events. It keeps a set of all seen event sequences and steers the execution towards
+ * executing events in previously unseen order. It does it by postponing execution of threads that
+ * would lead to an already seen sequence.
+ *
+ * If an event A occurs before event B in the sequence, this is how execution order looks like:
+ * Events: ... A ... B ...
+ * Events and instructions, guaranteed order:
+ * (instructions executed prior to A) A ... B (instructions executed after B)
+ *
+ * Each iteration has 3 parts (phases).
+ * Phase 1. Picking a previously seen event subsequence that we believe can have previously unseen
+ * continuations. Reproducing this sequence by pausing threads that would lead to other sequences.
+ * Phase 2. Trying to generate previously unseen continuation of the sequence from Phase 1. We need
+ * one new event after that sequence. All threads leading to seen continuations will be postponed
+ * for some short period of time. The phase ends once the new event is registered, or after the
+ * period of time ends (in which case we declare that the sequence can’t have new continuations).
+ * Phase 3. Releasing all threads and letting the test iteration run till its end.
+ *
+ * The iterations end when all seen paths have been declared “uncontinuable”.
+ *
+ * When we register event XXX:enter, we hold all other events until we register XXX:exit.
+ */
+public class RaceConditionReproducer implements RaceConditionTracker.EventProcessor {
+    private static final String TAG = "RaceConditionReproducer";
+    private static final long SHORT_TIMEOUT_MS = 2000;
+    private static final long LONG_TIMEOUT_MS = 60000;
+    // Handler used to resume postponed events.
+    private static final Handler POSTPONED_EVENT_RESUME_HANDLER = createEventResumeHandler();
+
+    private static Handler createEventResumeHandler() {
+        final HandlerThread thread = new HandlerThread("RaceConditionEventResumer");
+        thread.start();
+        return new Handler(thread.getLooper());
+    }
+
+    /**
+     * Event in a particular sequence of events. A node in the prefix tree of all seen event
+     * sequences.
+     */
+    private class EventNode {
+        // Events that were seen just after this event.
+        private final Map<String, EventNode> mNextEvents = new HashMap<>();
+        // Whether we believe that further iterations will not be able to add more events to
+        // mNextEvents.
+        private boolean mStoppedAddingChildren = true;
+
+        private void debugDump(StringBuilder sb, int indent, String name) {
+            for (int i = 0; i < indent; ++i) sb.append('.');
+            sb.append(!mStoppedAddingChildren ? "+" : "-");
+            sb.append(" : ");
+            sb.append(name);
+            if (mLastRegisteredEvent == this) sb.append(" <");
+            sb.append('\n');
+
+            for (String key : mNextEvents.keySet()) {
+                mNextEvents.get(key).debugDump(sb, indent + 2, key);
+            }
+        }
+
+        /** Number of leaves in the subtree with this node as a root. */
+        private int numberOfLeafNodes() {
+            if (mNextEvents.isEmpty()) return 1;
+
+            int leaves = 0;
+            for (String event : mNextEvents.keySet()) {
+                leaves += mNextEvents.get(event).numberOfLeafNodes();
+            }
+            return leaves;
+        }
+
+        /**
+         * Whether we believe that further iterations will not be able add nodes to the subtree with
+         * this node as a root.
+         */
+        private boolean stoppedAddingChildrenToTree() {
+            if (!mStoppedAddingChildren) return false;
+
+            for (String event : mNextEvents.keySet()) {
+                if (!mNextEvents.get(event).stoppedAddingChildrenToTree()) return false;
+            }
+            return true;
+        }
+
+        /**
+         * In the subtree with this node as a root, tries finding a node where we may have a
+         * chance to add new children.
+         * If succeeds, returns true and fills 'path' with the sequence of events to that node;
+         * otherwise returns false.
+         */
+        private boolean populatePathToGrowthPoint(List<String> path) {
+            for (String event : mNextEvents.keySet()) {
+                if (mNextEvents.get(event).populatePathToGrowthPoint(path)) {
+                    path.add(0, event);
+                    return true;
+                }
+            }
+            if (!mStoppedAddingChildren) {
+                // Mark that we have finished adding children. It will remain true if no new
+                // children are added, or will be set to false upon adding a new child.
+                mStoppedAddingChildren = true;
+                return true;
+            }
+            return false;
+        }
+    }
+
+    // Starting point of all event sequences; the root of the prefix tree representation all
+    // sequences generated by test iterations. A test iteration can add nodes int it.
+    private EventNode mRoot = new EventNode();
+    // During a test iteration, the last event that was registered.
+    private EventNode mLastRegisteredEvent;
+    // Length of the current sequence of registered events for the current test iteration.
+    private int mRegisteredEventCount = 0;
+    // During the first part of a test iteration, we go to a specific node under mRoot by
+    // 'playing back' mSequenceToFollow. During this part, all events that don't belong to this
+    // sequence get postponed.
+    private List<String> mSequenceToFollow = new ArrayList<>();
+    // Collection of events that got postponed, with corresponding wait objects used to let them go.
+    private Map<String, Semaphore> mPostponedEvents = new HashMap<>();
+    // Callback to run by POSTPONED_EVENT_RESUME_HANDLER, used to let go of all currently
+    // postponed events.
+    private Runnable mResumeAllEventsCallback;
+    // String representation of the sequence of events registered so far for the current test
+    // iteration. After registering any event, we output it to the log. The last output before
+    // the test failure can be later played back to reliable reproduce the exact sequence of
+    // events that broke the test.
+    // Format: EV1|EV2|...\EVN
+    private StringBuilder mCurrentSequence;
+    // When not null, we are in a repro mode. We run only one test iteration, and are trying to
+    // reproduce the event sequence represented by this string. The format is same as for
+    // mCurrentSequence.
+    private final String mReproString;
+
+    /* Constructor for a normal test. */
+    public RaceConditionReproducer() {
+        mReproString = null;
+    }
+
+    /**
+     * Constructor for reliably reproducing a race condition failure. The developer should find in
+     * the log the latest "Repro sequence:" record and locally modify the test by passing that
+     * string to the constructor. Running the test will have only one iteration that will reliably
+     * "play back" that sequence.
+     */
+    public RaceConditionReproducer(String reproString) {
+        mReproString = reproString;
+    }
+
+    public RaceConditionReproducer(String... reproSequence) {
+        this(String.join("|", reproSequence));
+    }
+
+    public synchronized String getCurrentSequenceString() {
+        return mCurrentSequence.toString();
+    }
+
+    /**
+     * Starts a new test iteration. Events reported via RaceConditionTracker.onEvent before this
+     * call will be ignored.
+     */
+    public synchronized void startIteration() {
+        mLastRegisteredEvent = mRoot;
+        mRegisteredEventCount = 0;
+        mCurrentSequence = new StringBuilder();
+        Log.d(TAG, "Repro sequence: " + mCurrentSequence);
+        mSequenceToFollow = mReproString != null ?
+                parseReproString(mReproString) : generateSequenceToFollowLocked();
+        Log.e(TAG, "---- Start of iteration; state:\n" + dumpStateLocked());
+        checkIfCompletedSequenceToFollowLocked();
+        RaceConditionTracker.setEventProcessor(this);
+    }
+
+    /**
+     * Ends a new test iteration. Events reported via RaceConditionTracker.onEvent after this call
+     * will be ignored.
+     * Returns whether we need more iterations.
+     */
+    public synchronized boolean finishIteration() {
+        RaceConditionTracker.setEventProcessor(null);
+        runResumeAllEventsCallbackLocked();
+        assertTrue("Non-empty postponed events", mPostponedEvents.isEmpty());
+        assertTrue("Last registered event is :enter", lastEventAsEnter() == null);
+
+        // No events came after mLastRegisteredEvent. It doesn't make sense to come to it again
+        // because we won't see new continuations.
+        mLastRegisteredEvent.mStoppedAddingChildren = true;
+        Log.e(TAG, "---- End of iteration; state:\n" + dumpStateLocked());
+        if (mReproString != null) {
+            assertTrue("Repro mode: failed to reproduce the sequence",
+                    mCurrentSequence.toString().startsWith(mReproString));
+        }
+        // If we are in a repro mode, we need only one iteration. Otherwise, continue if the tree
+        // has prospective growth points.
+        return mReproString == null && !mRoot.stoppedAddingChildrenToTree();
+    }
+
+    private static List<String> parseReproString(String reproString) {
+        return Arrays.asList(reproString.split("\\|"));
+    }
+
+    /**
+     * Called when the app issues an event.
+     */
+    @Override
+    public void onEvent(String event) {
+        final Semaphore waitObject = tryRegisterEvent(event);
+        if (waitObject != null) {
+            waitUntilCanRegister(event, waitObject);
+        }
+    }
+
+    /**
+     * Returns whether the last event was not an XXX:enter, or this event is a matching XXX:exit.
+     */
+    private boolean canRegisterEventNowLocked(String event) {
+        final String lastEventAsEnter = lastEventAsEnter();
+        final String thisEventAsExit = eventAsExit(event);
+
+        if (lastEventAsEnter != null) {
+            if (!lastEventAsEnter.equals(thisEventAsExit)) {
+                assertTrue("YYY:exit after XXX:enter", thisEventAsExit == null);
+                // Last event was :enter, but this event is not :exit.
+                return false;
+            }
+        } else {
+            // Previous event was not :enter.
+            assertTrue(":exit after a non-enter event", thisEventAsExit == null);
+        }
+        return true;
+    }
+
+    /**
+     * Registers an event issued by the app and returns null or decides that the event must be
+     * postponed, and returns an object to wait on.
+     */
+    private synchronized Semaphore tryRegisterEvent(String event) {
+        Log.d(TAG, "Event issued by the app: " + event);
+
+        if (!canRegisterEventNowLocked(event)) {
+            return createWaitObjectForPostponedEventLocked(event);
+        }
+
+        if (mRegisteredEventCount < mSequenceToFollow.size()) {
+            // We are in the first part of the iteration. We only register events that follow the
+            // mSequenceToFollow and postponing all other events.
+            if (event.equals(mSequenceToFollow.get(mRegisteredEventCount))) {
+                // The event is the next one expected in the sequence. Register it.
+                registerEventLocked(event);
+
+                // If there are postponed events that could continue the sequence, register them.
+                while (mRegisteredEventCount < mSequenceToFollow.size() &&
+                        mPostponedEvents.containsKey(
+                                mSequenceToFollow.get(mRegisteredEventCount))) {
+                    registerPostponedEventLocked(mSequenceToFollow.get(mRegisteredEventCount));
+                }
+
+                // Perhaps we just completed the required sequence...
+                checkIfCompletedSequenceToFollowLocked();
+            } else {
+                // The event is not the next one in the sequence. Postpone it.
+                return createWaitObjectForPostponedEventLocked(event);
+            }
+        } else if (mRegisteredEventCount == mSequenceToFollow.size()) {
+            // The second phase of the iteration. We have just registered the whole
+            // mSequenceToFollow, and want to add previously not seen continuations for the last
+            // node in the sequence aka 'growth point'.
+            if (!mLastRegisteredEvent.mNextEvents.containsKey(event) || mReproString != null) {
+                // The event was never seen as a continuation for the current node.
+                // Or we are in repro mode, in which case we are not in business of generating
+                // new sequences after we've played back the required sequence.
+                // Register it immediately.
+                registerEventLocked(event);
+            } else {
+                // The event was seen as a continuation for the current node. Postpone it, hoping
+                // that a new event will come from other threads.
+                return createWaitObjectForPostponedEventLocked(event);
+            }
+        } else {
+            // The third phase of the iteration. We are past the growth point and register
+            // everything that comes.
+            registerEventLocked(event);
+            // Register events that may have been postponed while waiting for an :exit event
+            // during the third phase. We don't do this if just registered event is :enter.
+            if (eventAsEnter(event) == null && mRegisteredEventCount > mSequenceToFollow.size()) {
+                registerPostponedEventsLocked(new HashSet<>(mPostponedEvents.keySet()));
+            }
+        }
+        return null;
+    }
+
+    /** Called when there are chances that we just have registered the whole mSequenceToFollow. */
+    private void checkIfCompletedSequenceToFollowLocked() {
+        if (mRegisteredEventCount == mSequenceToFollow.size()) {
+            // We just entered the second phase of the iteration. We have just registered the
+            // whole mSequenceToFollow, and want to add previously not seen continuations for the
+            // last node in the sequence aka 'growth point'. All seen continuations will be
+            // postponed for SHORT_TIMEOUT_MS. At the end of this time period, we'll let them go.
+            scheduleResumeAllEventsLocked();
+
+            // Among the events that were postponed during the first stage, there may be an event
+            // that wasn't seen after the current. If so, register it immediately because this
+            // creates a new sequence.
+            final Set<String> keys = new HashSet<>(mPostponedEvents.keySet());
+            keys.removeAll(mLastRegisteredEvent.mNextEvents.keySet());
+            if (!keys.isEmpty()) {
+                registerPostponedEventLocked(keys.iterator().next());
+            }
+        }
+    }
+
+    private Semaphore createWaitObjectForPostponedEventLocked(String event) {
+        final Semaphore waitObject = new Semaphore(0);
+        assertTrue("Event already postponed: " + event, !mPostponedEvents.containsKey(event));
+        mPostponedEvents.put(event, waitObject);
+        return waitObject;
+    }
+
+    private void waitUntilCanRegister(String event, Semaphore waitObject) {
+        try {
+            assertTrue("Never registered event: " + event,
+                    waitObject.tryAcquire(LONG_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        } catch (InterruptedException e) {
+            fail("Wait was interrupted");
+        }
+    }
+
+    /** Schedules resuming all postponed events after SHORT_TIMEOUT_MS */
+    private void scheduleResumeAllEventsLocked() {
+        assertTrue(mResumeAllEventsCallback == null);
+        mResumeAllEventsCallback = this::allEventsResumeCallback;
+        POSTPONED_EVENT_RESUME_HANDLER.postDelayed(mResumeAllEventsCallback, SHORT_TIMEOUT_MS);
+    }
+
+    private synchronized void allEventsResumeCallback() {
+        assertTrue("In callback, but callback is not set", mResumeAllEventsCallback != null);
+        mResumeAllEventsCallback = null;
+        registerPostponedEventsLocked(new HashSet<>(mPostponedEvents.keySet()));
+    }
+
+    private void registerPostponedEventsLocked(Collection<String> events) {
+        for (String event : events) {
+            registerPostponedEventLocked(event);
+            if (eventAsEnter(event) != null) {
+                // Once :enter is registered, switch to waiting for :exit to come. Won't register
+                // other postponed events.
+                break;
+            }
+        }
+    }
+
+    private void registerPostponedEventLocked(String event) {
+        mPostponedEvents.remove(event).release();
+        registerEventLocked(event);
+    }
+
+    /**
+     * If the last registered event was XXX:enter, returns XXX, otherwise, null.
+     */
+    private String lastEventAsEnter() {
+        return eventAsEnter(mCurrentSequence.substring(mCurrentSequence.lastIndexOf("|") + 1));
+    }
+
+    /**
+     * If the event is XXX:postfix, returns XXX, otherwise, null.
+     */
+    private static String prefixFromPostfixedEvent(String event, String postfix) {
+        final int columnPos = event.indexOf(':');
+        if (columnPos != -1 && postfix.equals(event.substring(columnPos + 1))) {
+            return event.substring(0, columnPos);
+        }
+        return null;
+    }
+
+    /**
+     * If the event is XXX:enter, returns XXX, otherwise, null.
+     */
+    private static String eventAsEnter(String event) {
+        return prefixFromPostfixedEvent(event, ENTER_POSTFIX);
+    }
+
+    /**
+     * If the event is XXX:exit, returns XXX, otherwise, null.
+     */
+    private static String eventAsExit(String event) {
+        return prefixFromPostfixedEvent(event, EXIT_POSTFIX);
+    }
+
+    private void registerEventLocked(String event) {
+        assertTrue(canRegisterEventNowLocked(event));
+
+        Log.d(TAG, "Actually registering event: " + event);
+        EventNode next = mLastRegisteredEvent.mNextEvents.get(event);
+        if (next == null) {
+            // This event wasn't seen after mLastRegisteredEvent.
+            next = new EventNode();
+            mLastRegisteredEvent.mNextEvents.put(event, next);
+            // The fact that we've added a new event after the previous one means that the
+            // previous event is still a growth point, unless this event is :exit, which means
+            // that the previous event is :enter.
+            mLastRegisteredEvent.mStoppedAddingChildren = eventAsExit(event) != null;
+        }
+
+        mLastRegisteredEvent = next;
+        mRegisteredEventCount++;
+
+        if (mCurrentSequence.length() > 0) mCurrentSequence.append("|");
+        mCurrentSequence.append(event);
+        Log.d(TAG, "Repro sequence: " + mCurrentSequence);
+    }
+
+    private void runResumeAllEventsCallbackLocked() {
+        if (mResumeAllEventsCallback != null) {
+            POSTPONED_EVENT_RESUME_HANDLER.removeCallbacks(mResumeAllEventsCallback);
+            mResumeAllEventsCallback.run();
+        }
+    }
+
+    private CharSequence dumpStateLocked() {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("Sequence to follow: ");
+        for (String event : mSequenceToFollow) sb.append(" " + event);
+        sb.append(".\n");
+        sb.append("Registered event count: " + mRegisteredEventCount);
+
+        sb.append("\nPostponed events: ");
+        for (String event : mPostponedEvents.keySet()) sb.append(" " + event);
+        sb.append(".");
+
+        sb.append("\nNodes: \n");
+        mRoot.debugDump(sb, 0, "");
+        return sb;
+    }
+
+    public int numberOfLeafNodes() {
+        return mRoot.numberOfLeafNodes();
+    }
+
+    private List<String> generateSequenceToFollowLocked() {
+        ArrayList<String> sequence = new ArrayList<>();
+        mRoot.populatePathToGrowthPoint(sequence);
+        return sequence;
+    }
+}
diff --git a/tests/src/com/android/launcher3/util/RaceConditionReproducerTest.java b/tests/src/com/android/launcher3/util/RaceConditionReproducerTest.java
new file mode 100644
index 0000000..3fc268e
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/RaceConditionReproducerTest.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class RaceConditionReproducerTest {
+    private final static String SOME_VALID_SEQUENCE_3_3 = "B1|A1|A2|B2|A3|B3";
+
+    private static int factorial(int n) {
+        int res = 1;
+        for (int i = 2; i <= n; ++i) res *= i;
+        return res;
+    }
+
+    private static void run3_3_TestAction() throws InterruptedException {
+        Thread tb = new Thread(() -> {
+            RaceConditionTracker.onEvent("B1");
+            RaceConditionTracker.onEvent("B2");
+            RaceConditionTracker.onEvent("B3");
+        });
+        tb.start();
+
+        RaceConditionTracker.onEvent("A1");
+        RaceConditionTracker.onEvent("A2");
+        RaceConditionTracker.onEvent("A3");
+
+        tb.join();
+    }
+
+    @Test
+    @Ignore // The test is too long for continuous testing.
+    // 2 threads, 3 events each.
+    public void test3_3() throws Exception {
+        final RaceConditionReproducer eventProcessor = new RaceConditionReproducer();
+        boolean sawTheValidSequence = false;
+
+        for (; ; ) {
+            eventProcessor.startIteration();
+            run3_3_TestAction();
+            final boolean needMoreIterations = eventProcessor.finishIteration();
+
+            sawTheValidSequence = sawTheValidSequence ||
+                    SOME_VALID_SEQUENCE_3_3.equals(eventProcessor.getCurrentSequenceString());
+
+            if (!needMoreIterations) break;
+        }
+
+        assertEquals("Wrong number of leaf nodes",
+                factorial(3 + 3) / (factorial(3) * factorial(3)),
+                eventProcessor.numberOfLeafNodes());
+        assertTrue(sawTheValidSequence);
+    }
+
+    @Test
+    @Ignore // The test is too long for continuous testing.
+    // 2 threads, 3 events, including enter-exit pairs each.
+    public void test3_3_enter_exit() throws Exception {
+        final RaceConditionReproducer eventProcessor = new RaceConditionReproducer();
+        boolean sawTheValidSequence = false;
+
+        for (; ; ) {
+            eventProcessor.startIteration();
+            Thread tb = new Thread(() -> {
+                RaceConditionTracker.onEvent("B1:enter");
+                RaceConditionTracker.onEvent("B1:exit");
+                RaceConditionTracker.onEvent("B2");
+                RaceConditionTracker.onEvent("B3:enter");
+                RaceConditionTracker.onEvent("B3:exit");
+            });
+            tb.start();
+
+            RaceConditionTracker.onEvent("A1");
+            RaceConditionTracker.onEvent("A2:enter");
+            RaceConditionTracker.onEvent("A2:exit");
+            RaceConditionTracker.onEvent("A3:enter");
+            RaceConditionTracker.onEvent("A3:exit");
+
+            tb.join();
+            final boolean needMoreIterations = eventProcessor.finishIteration();
+
+            sawTheValidSequence = sawTheValidSequence ||
+                    "B1:enter|B1:exit|A1|A2:enter|A2:exit|B2|A3:enter|A3:exit|B3:enter|B3:exit".
+                            equals(eventProcessor.getCurrentSequenceString());
+
+            if (!needMoreIterations) break;
+        }
+
+        assertEquals("Wrong number of leaf nodes",
+                factorial(3 + 3) / (factorial(3) * factorial(3)),
+                eventProcessor.numberOfLeafNodes());
+        assertTrue(sawTheValidSequence);
+    }
+
+    @Test
+    // 2 threads, 3 events each; reproducing a particular event sequence.
+    public void test3_3_ReproMode() throws Exception {
+        final RaceConditionReproducer eventProcessor = new RaceConditionReproducer(
+                SOME_VALID_SEQUENCE_3_3);
+
+        eventProcessor.startIteration();
+        run3_3_TestAction();
+        assertTrue(!eventProcessor.finishIteration());
+        assertEquals(SOME_VALID_SEQUENCE_3_3, eventProcessor.getCurrentSequenceString());
+
+        assertEquals("Wrong number of leaf nodes", 1, eventProcessor.numberOfLeafNodes());
+    }
+
+    @Test
+    @Ignore // The test is too long for continuous testing.
+    // 2 threads with 2 events; 1 thread with 1 event.
+    public void test2_1_2() throws Exception {
+        final RaceConditionReproducer eventProcessor = new RaceConditionReproducer();
+
+        for (; ; ) {
+            eventProcessor.startIteration();
+            Thread tb = new Thread(() -> {
+                RaceConditionTracker.onEvent("B1");
+                RaceConditionTracker.onEvent("B2");
+            });
+            tb.start();
+
+            Thread tc = new Thread(() -> {
+                RaceConditionTracker.onEvent("C1");
+            });
+            tc.start();
+
+            RaceConditionTracker.onEvent("A1");
+            RaceConditionTracker.onEvent("A2");
+
+            tb.join();
+            tc.join();
+
+            if (!eventProcessor.finishIteration()) break;
+        }
+
+        assertEquals("Wrong number of leaf nodes",
+                factorial(2 + 2 + 1) / (factorial(2) * factorial(2) * factorial(1)),
+                eventProcessor.numberOfLeafNodes());
+    }
+
+    @Test
+    @Ignore // The test is too long for continuous testing.
+    // 2 threads with 2 events; 1 thread with 1 event. Includes enter-exit pairs.
+    public void test2_1_2_enter_exit() throws Exception {
+        final RaceConditionReproducer eventProcessor = new RaceConditionReproducer();
+
+        for (; ; ) {
+            eventProcessor.startIteration();
+            Thread tb = new Thread(() -> {
+                RaceConditionTracker.onEvent("B1:enter");
+                RaceConditionTracker.onEvent("B1:exit");
+                RaceConditionTracker.onEvent("B2:enter");
+                RaceConditionTracker.onEvent("B2:exit");
+            });
+            tb.start();
+
+            Thread tc = new Thread(() -> {
+                RaceConditionTracker.onEvent("C1:enter");
+                RaceConditionTracker.onEvent("C1:exit");
+            });
+            tc.start();
+
+            RaceConditionTracker.onEvent("A1:enter");
+            RaceConditionTracker.onEvent("A1:exit");
+            RaceConditionTracker.onEvent("A2:enter");
+            RaceConditionTracker.onEvent("A2:exit");
+
+            tb.join();
+            tc.join();
+
+            if (!eventProcessor.finishIteration()) break;
+        }
+
+        assertEquals("Wrong number of leaf nodes",
+                factorial(2 + 2 + 1) / (factorial(2) * factorial(2) * factorial(1)),
+                eventProcessor.numberOfLeafNodes());
+    }
+}
diff --git a/tests/src/com/android/launcher3/util/TestLauncherProvider.java b/tests/src/com/android/launcher3/util/TestLauncherProvider.java
deleted file mode 100644
index 1d6c18b..0000000
--- a/tests/src/com/android/launcher3/util/TestLauncherProvider.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package com.android.launcher3.util;
-
-import android.content.Context;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
-
-import com.android.launcher3.LauncherProvider;
-
-/**
- * An extension of LauncherProvider backed up by in-memory database.
- */
-public class TestLauncherProvider extends LauncherProvider {
-
-    @Override
-    public boolean onCreate() {
-        return true;
-    }
-
-    @Override
-    protected synchronized void createDbIfNotExists() {
-        if (mOpenHelper == null) {
-            mOpenHelper = new MyDatabaseHelper(getContext());
-        }
-    }
-
-    public SQLiteOpenHelper getHelper() {
-        createDbIfNotExists();
-        return mOpenHelper;
-    }
-
-    @Override
-    protected void notifyListeners() { }
-
-    private static class MyDatabaseHelper extends DatabaseHelper {
-        public MyDatabaseHelper(Context context) {
-            super(context, null, null);
-            initIds();
-        }
-
-        @Override
-        public long getDefaultUserSerial() {
-            return 0;
-        }
-
-        @Override
-        protected void onEmptyDbCreated() { }
-
-        @Override
-        protected void handleOneTimeDataUpgrade(SQLiteDatabase db) { }
-    }
-}
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/util/TestUtil.java b/tests/src/com/android/launcher3/util/TestUtil.java
new file mode 100644
index 0000000..55e5744
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/TestUtil.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util;
+
+import static androidx.test.InstrumentationRegistry.getContext;
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import android.content.res.Resources;
+
+import androidx.test.uiautomator.UiDevice;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class TestUtil {
+    public static final String DUMMY_PACKAGE = "com.example.android.aardwolf";
+
+    public static void installDummyApp() throws IOException {
+        // Copy apk from resources to a local file and install from there.
+        final Resources resources = getContext().getResources();
+        final InputStream in = resources.openRawResource(
+                resources.getIdentifier("aardwolf_dummy_app",
+                        "raw", getContext().getPackageName()));
+        final String apkFilename = getInstrumentation().getTargetContext().
+                getFilesDir().getPath() + "/dummy_app.apk";
+
+        final FileOutputStream out = new FileOutputStream(apkFilename);
+        byte[] buff = new byte[1024];
+        int read;
+
+        while ((read = in.read(buff)) > 0) {
+            out.write(buff, 0, read);
+        }
+        in.close();
+        out.close();
+
+        UiDevice.getInstance(getInstrumentation()).executeShellCommand("pm install " + apkFilename);
+    }
+
+    public static void uninstallDummyApp() throws IOException {
+        UiDevice.getInstance(getInstrumentation()).executeShellCommand(
+                "pm uninstall " + DUMMY_PACKAGE);
+    }
+}
diff --git a/tests/src/com/android/launcher3/util/Wait.java b/tests/src/com/android/launcher3/util/Wait.java
index f9e53ba..593cce8 100644
--- a/tests/src/com/android/launcher3/util/Wait.java
+++ b/tests/src/com/android/launcher3/util/Wait.java
@@ -2,6 +2,8 @@
 
 import android.os.SystemClock;
 
+import org.junit.Assert;
+
 /**
  * A utility class for waiting for a condition to be true.
  */
@@ -9,19 +11,19 @@
 
     private static final long DEFAULT_SLEEP_MS = 200;
 
-    public static boolean atMost(Condition condition, long timeout) {
-        return atMost(condition, timeout, DEFAULT_SLEEP_MS);
+    public static void atMost(String message, Condition condition, long timeout) {
+        atMost(message, condition, timeout, DEFAULT_SLEEP_MS);
     }
 
-    public static boolean atMost(Condition condition, long timeout, long sleepMillis) {
+    public static void atMost(String message, Condition condition, long timeout, long sleepMillis) {
         long endTime = SystemClock.uptimeMillis() + timeout;
         while (SystemClock.uptimeMillis() < endTime) {
             try {
                 if (condition.isTrue()) {
-                    return true;
+                    return;
                 }
             } catch (Throwable t) {
-                // Ignore
+                throw new RuntimeException(t);
             }
             SystemClock.sleep(sleepMillis);
         }
@@ -29,11 +31,11 @@
         // Check once more before returning false.
         try {
             if (condition.isTrue()) {
-                return true;
+                return;
             }
         } catch (Throwable t) {
-            // Ignore
+            throw new RuntimeException(t);
         }
-        return false;
+        Assert.fail(message);
     }
 }
diff --git a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
new file mode 100644
index 0000000..eef2f24
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
@@ -0,0 +1,59 @@
+package com.android.launcher3.util.rule;
+
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import android.util.Log;
+
+import androidx.test.uiautomator.UiDevice;
+
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+
+public class FailureWatcher extends TestWatcher {
+    private static final String TAG = "FailureWatcher";
+    private static int sScreenshotCount = 0;
+    final private UiDevice mDevice;
+
+    public FailureWatcher(UiDevice device) {
+        mDevice = device;
+    }
+
+    private void dumpViewHierarchy() {
+        final ByteArrayOutputStream stream = new ByteArrayOutputStream();
+        try {
+            mDevice.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 failed(Throwable e, Description description) {
+        if (mDevice == null) return;
+        final String pathname = getInstrumentation().getTargetContext().
+                getFilesDir().getPath() + "/TaplTestScreenshot" + sScreenshotCount++ + ".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();
+
+        try {
+            final String dumpsysResult = mDevice.executeShellCommand(
+                    "dumpsys activity service TouchInteractionService");
+            Log.d(TAG, "TouchInteractionService: " + dumpsysResult);
+        } catch (IOException ex) {
+        }
+
+        mDevice.takeScreenshot(new File(pathname));
+    }
+}
diff --git a/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java b/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java
index edd152a..2aba7a5 100644
--- a/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java
+++ b/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java
@@ -15,12 +15,16 @@
  */
 package com.android.launcher3.util.rule;
 
+import static com.android.launcher3.tapl.TestHelpers.getHomeIntentInPackage;
+
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static androidx.test.InstrumentationRegistry.getTargetContext;
+
 import android.app.Activity;
 import android.app.Application;
 import android.app.Application.ActivityLifecycleCallbacks;
-import android.content.Intent;
 import android.os.Bundle;
-import android.support.test.InstrumentationRegistry;
+import androidx.test.InstrumentationRegistry;
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.Workspace.ItemOperator;
@@ -65,19 +69,7 @@
      * Starts the launcher activity in the target package.
      */
     public void startLauncher() {
-        InstrumentationRegistry.getInstrumentation().startActivitySync(getHomeIntent());
-    }
-
-    public void returnToHome() {
-        InstrumentationRegistry.getTargetContext().startActivity(getHomeIntent());
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-    }
-
-    public static Intent getHomeIntent() {
-        return new Intent(Intent.ACTION_MAIN)
-                .addCategory(Intent.CATEGORY_HOME)
-                .setPackage(InstrumentationRegistry.getTargetContext().getPackageName())
-                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        getInstrumentation().startActivitySync(getHomeIntentInPackage(getTargetContext()));
     }
 
     private class MyStatement extends Statement implements ActivityLifecycleCallbacks {
diff --git a/tests/src/com/android/launcher3/util/rule/ShellCommandRule.java b/tests/src/com/android/launcher3/util/rule/ShellCommandRule.java
index dba2d71..0ec0f02 100644
--- a/tests/src/com/android/launcher3/util/rule/ShellCommandRule.java
+++ b/tests/src/com/android/launcher3/util/rule/ShellCommandRule.java
@@ -15,17 +15,20 @@
  */
 package com.android.launcher3.util.rule;
 
+import static com.android.launcher3.tapl.TestHelpers.getLauncherInMyProcess;
+
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
 import android.content.ComponentName;
 import android.content.pm.ActivityInfo;
-import android.os.ParcelFileDescriptor;
-import android.support.test.InstrumentationRegistry;
 
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
 
-import java.io.FileInputStream;
-import java.io.IOException;
+import androidx.annotation.Nullable;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.uiautomator.UiDevice;
 
 /**
  * Test rule which executes a shell command at the start of the test.
@@ -33,58 +36,55 @@
 public class ShellCommandRule implements TestRule {
 
     private final String mCmd;
+    private final String mRevertCommand;
 
-    public ShellCommandRule(String cmd) {
+    public ShellCommandRule(String cmd, @Nullable String revertCommand) {
         mCmd = cmd;
+        mRevertCommand = revertCommand;
     }
 
     @Override
     public Statement apply(Statement base, Description description) {
-        return new MyStatement(base, mCmd);
-    }
-
-    public static void runShellCommand(String command) throws IOException {
-        ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation().getUiAutomation()
-                .executeShellCommand(command);
-
-        // Read the input stream fully.
-        FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
-        while (fis.read() != -1);
-        fis.close();
-    }
-
-    private static class MyStatement extends Statement {
-        private final Statement mBase;
-        private final String mCmd;
-
-        public MyStatement(Statement base, String cmd) {
-            mBase = base;
-            mCmd = cmd;
-        }
-
-        @Override
-        public void evaluate() throws Throwable {
-            runShellCommand(mCmd);
-            mBase.evaluate();
-        }
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                UiDevice.getInstance(getInstrumentation()).executeShellCommand(mCmd);
+                try {
+                    base.evaluate();
+                } finally {
+                    if (mRevertCommand != null) {
+                        UiDevice.getInstance(getInstrumentation()).executeShellCommand(mRevertCommand);
+                    }
+                }
+            }
+        };
     }
 
     /**
      * Grants the launcher permission to bind widgets.
      */
-    public static ShellCommandRule grandWidgetBind() {
+    public static ShellCommandRule grantWidgetBind() {
         return new ShellCommandRule("appwidget grantbind --package "
-                + InstrumentationRegistry.getTargetContext().getPackageName());
+                + InstrumentationRegistry.getTargetContext().getPackageName(), null);
     }
 
     /**
      * Sets the target launcher as default launcher.
      */
     public static ShellCommandRule setDefaultLauncher() {
-        ActivityInfo launcher = InstrumentationRegistry.getTargetContext().getPackageManager()
-                .queryIntentActivities(LauncherActivityRule.getHomeIntent(), 0).get(0)
-                .activityInfo;
-        return new ShellCommandRule("cmd package set-home-activity " +
-                new ComponentName(launcher.packageName, launcher.name).flattenToString());
+        return new ShellCommandRule(getLauncherCommand(getLauncherInMyProcess()), null);
+    }
+
+    public static String getLauncherCommand(ActivityInfo launcher) {
+        return "cmd package set-home-activity " +
+                new ComponentName(launcher.packageName, launcher.name).flattenToString();
+    }
+
+    /**
+     * Disables heads up notification for the duration of the test
+     */
+    public static ShellCommandRule disableHeadsUpNotification() {
+        return new ShellCommandRule("settings put global heads_up_notifications_enabled 0",
+                "settings put global heads_up_notifications_enabled 1");
     }
 }
diff --git a/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java b/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java
index 0185f13..a31d8a6 100644
--- a/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java
+++ b/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java
@@ -15,17 +15,20 @@
  */
 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 android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
-import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.v7.widget.RecyclerView;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 import android.view.LayoutInflater;
 
-import com.android.launcher3.IconCache;
+import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.WidgetPreviewLoader;
@@ -43,10 +46,7 @@
 import java.util.ArrayList;
 import java.util.Map;
 
-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 androidx.recyclerview.widget.RecyclerView;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -128,11 +128,10 @@
         if (num <= 0) return result;
 
         MultiHashMap<PackageItemInfo, WidgetItem> newMap = new MultiHashMap();
-        PackageManager pm = mContext.getPackageManager();
         AppWidgetManagerCompat widgetManager = AppWidgetManagerCompat.getInstance(mContext);
         for (AppWidgetProviderInfo widgetInfo : widgetManager.getAllProviders(null)) {
             WidgetItem wi = new WidgetItem(LauncherAppWidgetProviderInfo
-                    .fromProviderInfo(mContext, widgetInfo), pm, mTestProfile);
+                    .fromProviderInfo(mContext, widgetInfo), mTestProfile, mIconCache);
 
             PackageItemInfo pInfo = new PackageItemInfo(wi.componentName.getPackageName());
             pInfo.title = pInfo.packageName;
diff --git a/tests/tapl/AndroidManifest.xml b/tests/tapl/AndroidManifest.xml
new file mode 100644
index 0000000..0207e2b
--- /dev/null
+++ b/tests/tapl/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<manifest
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.launcher3.tapl"
+>
+
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
+</manifest>
diff --git a/tests/tapl/README b/tests/tapl/README
new file mode 100644
index 0000000..a35d792
--- /dev/null
+++ b/tests/tapl/README
@@ -0,0 +1 @@
+http://go/tapl
diff --git a/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java b/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java
new file mode 100644
index 0000000..7f561a2
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java
@@ -0,0 +1,41 @@
+/*
+ * 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.tapl;
+
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiObject2;
+
+public class AddToHomeScreenPrompt {
+    private final LauncherInstrumentation mLauncher;
+    private final UiObject2 mWidgetCell;
+
+    AddToHomeScreenPrompt(LauncherInstrumentation launcher) {
+        mLauncher = launcher;
+        mWidgetCell = launcher.waitForLauncherObject(By.clazz(
+                "com.android.launcher3.widget.WidgetCell"));
+        mLauncher.assertNotNull("Can't find widget cell object", mWidgetCell);
+    }
+
+    public void addAutomatically() {
+        mLauncher.waitForObjectInContainer(
+                mWidgetCell.getParent().getParent().getParent().getParent(),
+                By.text(LauncherInstrumentation.isAvd()
+                        ? "ADD AUTOMATICALLY"
+                        : "Add automatically")).
+                click();
+    }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
new file mode 100644
index 0000000..9ff354a
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -0,0 +1,214 @@
+/*
+ * 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.tapl;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.Direction;
+import androidx.test.uiautomator.UiObject2;
+
+import com.android.launcher3.ResourceUtils;
+import com.android.launcher3.testing.TestProtocol;
+
+/**
+ * Operations on AllApps opened from Home. Also a parent for All Apps opened from Overview.
+ */
+public class AllApps extends LauncherInstrumentation.VisibleContainer {
+    private static final int MAX_SCROLL_ATTEMPTS = 40;
+
+    private final int mHeight;
+
+    AllApps(LauncherInstrumentation launcher) {
+        super(launcher);
+        final UiObject2 allAppsContainer = verifyActiveContainer();
+        mHeight = allAppsContainer.getVisibleBounds().height();
+        final UiObject2 appListRecycler = mLauncher.waitForObjectInContainer(allAppsContainer,
+                "apps_list_view");
+        // Wait for the recycler to populate.
+        mLauncher.waitForObjectInContainer(appListRecycler, By.clazz(TextView.class));
+        verifyNotFrozen("All apps freeze flags upon opening all apps");
+    }
+
+    @Override
+    protected LauncherInstrumentation.ContainerType getContainerType() {
+        return LauncherInstrumentation.ContainerType.ALL_APPS;
+    }
+
+    private boolean hasClickableIcon(
+            UiObject2 allAppsContainer, UiObject2 appListRecycler, BySelector appIconSelector) {
+        final UiObject2 icon = appListRecycler.findObject(appIconSelector);
+        if (icon == null) {
+            LauncherInstrumentation.log("hasClickableIcon: icon not visible");
+            return false;
+        }
+        final Rect iconBounds = icon.getVisibleBounds();
+        LauncherInstrumentation.log("hasClickableIcon: icon bounds: " + iconBounds);
+        if (iconCenterInSearchBox(allAppsContainer, icon)) {
+            LauncherInstrumentation.log("hasClickableIcon: icon center is under search box");
+            return false;
+        }
+        LauncherInstrumentation.log("hasClickableIcon: icon is clickable");
+        return true;
+    }
+
+    private boolean iconCenterInSearchBox(UiObject2 allAppsContainer, UiObject2 icon) {
+        final Point iconCenter = icon.getVisibleCenter();
+        return getSearchBox(allAppsContainer).getVisibleBounds().contains(
+                iconCenter.x, iconCenter.y);
+    }
+
+    /**
+     * Finds an icon. Fails if the icon doesn't exist. Scrolls the app list when needed to make
+     * sure the icon is visible.
+     *
+     * @param appName name of the app.
+     * @return The app.
+     */
+    @NonNull
+    public AppIcon getAppIcon(String appName) {
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "getting app icon " + appName + " on all apps")) {
+            final UiObject2 allAppsContainer = verifyActiveContainer();
+            final UiObject2 appListRecycler = mLauncher.waitForObjectInContainer(allAppsContainer,
+                    "apps_list_view");
+            allAppsContainer.setGestureMargins(
+                    0,
+                    getSearchBox(allAppsContainer).getVisibleBounds().bottom + 1,
+                    0,
+                    ResourceUtils.getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE,
+                            mLauncher.getResources()) + 1);
+            final BySelector appIconSelector = AppIcon.getAppIconSelector(appName, mLauncher);
+            if (!hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector)) {
+                scrollBackToBeginning();
+                int attempts = 0;
+                int scroll = getScroll(allAppsContainer);
+                try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("scrolled")) {
+                    while (!hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector)) {
+                        mLauncher.scroll(allAppsContainer, Direction.DOWN, 0.8f, null, 50);
+                        final int newScroll = getScroll(allAppsContainer);
+                        if (newScroll == scroll) break;
+
+                        mLauncher.assertTrue(
+                                "Exceeded max scroll attempts: " + MAX_SCROLL_ATTEMPTS,
+                                ++attempts <= MAX_SCROLL_ATTEMPTS);
+                        verifyActiveContainer();
+                        scroll = newScroll;
+                    }
+                }
+                verifyActiveContainer();
+            }
+
+            mLauncher.assertTrue("Unable to scroll to a clickable icon: " + appName,
+                    hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector));
+
+            final UiObject2 appIcon = mLauncher.getObjectInContainer(appListRecycler,
+                    appIconSelector);
+            return new AppIcon(mLauncher, appIcon);
+        }
+    }
+
+    private void scrollBackToBeginning() {
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to scroll back in all apps")) {
+            LauncherInstrumentation.log("Scrolling to the beginning");
+            final UiObject2 allAppsContainer = verifyActiveContainer();
+            final UiObject2 searchBox = getSearchBox(allAppsContainer);
+
+            int attempts = 0;
+            final Rect margins = new Rect(0, searchBox.getVisibleBounds().bottom + 1, 0, 5);
+
+            for (int scroll = getScroll(allAppsContainer);
+                    scroll != 0;
+                    scroll = getScroll(allAppsContainer)) {
+                mLauncher.assertTrue("Negative scroll position", scroll > 0);
+
+                mLauncher.assertTrue(
+                        "Exceeded max scroll attempts: " + MAX_SCROLL_ATTEMPTS,
+                        ++attempts <= MAX_SCROLL_ATTEMPTS);
+
+                mLauncher.scroll(allAppsContainer, Direction.UP, 1, margins, 50);
+            }
+
+            try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("scrolled up")) {
+                verifyActiveContainer();
+            }
+        }
+    }
+
+    private int getScroll(UiObject2 allAppsContainer) {
+        return mLauncher.getAnswerFromLauncher(allAppsContainer, TestProtocol.GET_SCROLL_MESSAGE).
+                getInt(TestProtocol.SCROLL_Y_FIELD, -1);
+    }
+
+    private UiObject2 getSearchBox(UiObject2 allAppsContainer) {
+        return mLauncher.waitForObjectInContainer(allAppsContainer, "search_container_all_apps");
+    }
+
+    /**
+     * Flings forward (down) and waits the fling's end.
+     */
+    public void flingForward() {
+        try (LauncherInstrumentation.Closable c =
+                     mLauncher.addContextLayer("want to fling forward in all apps")) {
+            final UiObject2 allAppsContainer = verifyActiveContainer();
+            // Start the gesture in the center to avoid starting at elements near the top.
+            mLauncher.scroll(
+                    allAppsContainer, Direction.DOWN, 1, new Rect(0, 0, 0, mHeight / 2), 10);
+            verifyActiveContainer();
+        }
+    }
+
+    /**
+     * Flings backward (up) and waits the fling's end.
+     */
+    public void flingBackward() {
+        try (LauncherInstrumentation.Closable c =
+                     mLauncher.addContextLayer("want to fling backward in all apps")) {
+            final UiObject2 allAppsContainer = verifyActiveContainer();
+            // Start the gesture in the center, for symmetry with forward.
+            mLauncher.scroll(
+                    allAppsContainer, Direction.UP, 1, new Rect(0, mHeight / 2, 0, 0), 10);
+            verifyActiveContainer();
+        }
+    }
+
+    /**
+     * Freezes updating app list upon app install/uninstall/update.
+     */
+    public void freeze() {
+        mLauncher.getTestInfo(TestProtocol.REQUEST_FREEZE_APP_LIST);
+    }
+
+    /**
+     * Resumes updating app list upon app install/uninstall/update.
+     */
+    public void unfreeze() {
+        mLauncher.getTestInfo(TestProtocol.REQUEST_UNFREEZE_APP_LIST);
+        verifyNotFrozen("All apps freeze flags upon unfreezing");
+    }
+
+    private void verifyNotFrozen(String message) {
+        mLauncher.assertEquals(message, 0, mLauncher.getTestInfo(
+                TestProtocol.REQUEST_APP_LIST_FREEZE_FLAGS).
+                getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD));
+    }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/AllAppsFromHome.java b/tests/tapl/com/android/launcher3/tapl/AllAppsFromHome.java
deleted file mode 100644
index 02f8183..0000000
--- a/tests/tapl/com/android/launcher3/tapl/AllAppsFromHome.java
+++ /dev/null
@@ -1,142 +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.tapl;
-
-import android.support.annotation.NonNull;
-import android.support.test.uiautomator.BySelector;
-import android.support.test.uiautomator.Direction;
-import android.support.test.uiautomator.UiObject2;
-
-/**
- * Operations on AllApps opened from Home.
- */
-public final class AllAppsFromHome {
-    private static final int MAX_SCROLL_ATTEMPTS = 40;
-    private static final int MIN_INTERACT_SIZE = 100;
-    private static final int FLING_SPEED = 12000;
-
-    private final Launcher mLauncher;
-    private final int mHeight;
-
-    AllAppsFromHome(Launcher launcher) {
-        mLauncher = launcher;
-        final UiObject2 allAppsContainer = assertState();
-        mHeight = allAppsContainer.getVisibleBounds().height();
-    }
-
-    /**
-     * Asserts that we are in all apps.
-     *
-     * @return All apps container.
-     */
-    @NonNull
-    private UiObject2 assertState() {
-        return mLauncher.assertState(Launcher.State.ALL_APPS);
-    }
-
-    /**
-     * Finds an icon. Fails if the icon doesn't exist. Scrolls the app list when needed to make
-     * sure the icon is visible.
-     *
-     * @param appName name of the app.
-     * @return The app.
-     */
-    @NonNull
-    public AppIcon getAppIcon(String appName) {
-        final UiObject2 allAppsContainer = assertState();
-        final BySelector appIconSelector = AppIcon.getAppIconSelector(appName);
-        if (!allAppsContainer.hasObject(appIconSelector)) {
-            scrollBackToBeginning();
-            int attempts = 0;
-            while (!allAppsContainer.hasObject(appIconSelector) &&
-                    allAppsContainer.scroll(Direction.DOWN, 0.8f)) {
-                mLauncher.assertTrue("Exceeded max scroll attempts: " + MAX_SCROLL_ATTEMPTS,
-                        ++attempts <= MAX_SCROLL_ATTEMPTS);
-                assertState();
-            }
-        }
-        assertState();
-
-        final UiObject2 appIcon = mLauncher.getObjectInContainer(allAppsContainer, appIconSelector);
-        ensureIconVisible(appIcon, allAppsContainer);
-        return new AppIcon(mLauncher, appIcon);
-    }
-
-    private void scrollBackToBeginning() {
-        final UiObject2 allAppsContainer = assertState();
-
-        int attempts = 0;
-        allAppsContainer.setGestureMargins(5, 500, 5, 5);
-
-        while (allAppsContainer.scroll(Direction.UP, 0.5f)) {
-            mLauncher.waitForIdle();
-            assertState();
-
-            mLauncher.assertTrue("Exceeded max scroll attempts: " + MAX_SCROLL_ATTEMPTS,
-                    ++attempts <= MAX_SCROLL_ATTEMPTS);
-        }
-
-        mLauncher.waitForIdle();
-        assertState();
-    }
-
-    private void ensureIconVisible(UiObject2 appIcon, UiObject2 allAppsContainer) {
-        final int appHeight = appIcon.getVisibleBounds().height();
-        if (appHeight < MIN_INTERACT_SIZE) {
-            // Try to figure out how much percentage of the container needs to be scrolled in order
-            // to reveal the app icon to have the MIN_INTERACT_SIZE
-            final float pct = Math.max(((float) (MIN_INTERACT_SIZE - appHeight)) / mHeight, 0.2f);
-            allAppsContainer.scroll(Direction.DOWN, pct);
-            mLauncher.waitForIdle();
-            assertState();
-        }
-    }
-
-    /**
-     * Flings forward (down) and waits the fling's end.
-     */
-    public void flingForward() {
-        final UiObject2 allAppsContainer = assertState();
-        // Start the gesture in the center to avoid starting at elements near the top.
-        allAppsContainer.setGestureMargins(0, 0, 0, mHeight / 2);
-        allAppsContainer.fling(Direction.DOWN, FLING_SPEED);
-        assertState();
-    }
-
-    /**
-     * Flings backward (up) and waits the fling's end.
-     */
-    public void flingBackward() {
-        final UiObject2 allAppsContainer = assertState();
-        // Start the gesture in the center, for symmetry with forward.
-        allAppsContainer.setGestureMargins(0, mHeight / 2, 0, 0);
-        allAppsContainer.fling(Direction.UP, FLING_SPEED);
-        assertState();
-    }
-
-    /**
-     * Gets the UI object for AllApps.
-     * Used by NexusLauncherStrategy.openAllApps(). No one else should use it.
-     *
-     * @return container object.
-     */
-    @Deprecated
-    @NonNull
-    public UiObject2 getObjectDeprecated() {
-        return assertState();
-    }
-}
diff --git a/tests/tapl/com/android/launcher3/tapl/AllAppsFromOverview.java b/tests/tapl/com/android/launcher3/tapl/AllAppsFromOverview.java
index cba7086..f48d4dd 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllAppsFromOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllAppsFromOverview.java
@@ -16,30 +16,23 @@
 
 package com.android.launcher3.tapl;
 
+import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
+
 import android.graphics.Point;
-import android.support.annotation.NonNull;
-import android.support.test.uiautomator.UiObject2;
+
+import androidx.annotation.NonNull;
+import androidx.test.uiautomator.UiObject2;
+
+import com.android.launcher3.testing.TestProtocol;
 
 /**
  * Operations on AllApps opened from Overview.
- * Scroll gestures that are OK for {@link AllAppsFromHome} may close it, so they are not supported.
  */
-public final class AllAppsFromOverview {
-    private final Launcher mLauncher;
+public final class AllAppsFromOverview extends AllApps {
 
-    AllAppsFromOverview(Launcher launcher) {
-        mLauncher = launcher;
-        assertState();
-    }
-
-    /**
-     * Asserts that we are in all apps.
-     *
-     * @return All apps container.
-     */
-    @NonNull
-    private UiObject2 assertState() {
-        return mLauncher.assertState(Launcher.State.ALL_APPS);
+    AllAppsFromOverview(LauncherInstrumentation launcher) {
+        super(launcher);
+        verifyActiveContainer();
     }
 
     /**
@@ -49,15 +42,24 @@
      */
     @NonNull
     public Overview switchBackToOverview() {
-        final UiObject2 allAppsContainer = assertState();
-        // Swipe from the search box to the bottom.
-        final UiObject2 qsb = mLauncher.waitForObjectInContainer(
-                allAppsContainer, "search_container_all_apps");
-        final Point start = qsb.getVisibleCenter();
-        final int endY = (int) (mLauncher.getDevice().getDisplayHeight() * 0.6);
-        mLauncher.swipe(start.x, start.y, start.x, endY, (endY - start.y) / 100);  // 100 px/step
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to switch back from all apps to overview")) {
+            final UiObject2 allAppsContainer = verifyActiveContainer();
+            // Swipe from the search box to the bottom.
+            final UiObject2 qsb = mLauncher.waitForObjectInContainer(
+                    allAppsContainer, "search_container_all_apps");
+            final Point start = qsb.getVisibleCenter();
+            final int swipeHeight = mLauncher.getTestInfo(
+                    TestProtocol.REQUEST_ALL_APPS_TO_OVERVIEW_SWIPE_HEIGHT).
+                    getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
 
-        return new Overview(mLauncher);
+            final int endY = start.y + swipeHeight;
+            LauncherInstrumentation.log("AllAppsFromOverview.switchBackToOverview before swipe");
+            mLauncher.swipeToState(start.x, start.y, start.x, endY, 60, OVERVIEW_STATE_ORDINAL);
+
+            try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("swiped down")) {
+                return new Overview(mLauncher);
+            }
+        }
     }
-
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
index 73a74f2..44fc3f7 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
@@ -16,38 +16,43 @@
 
 package com.android.launcher3.tapl;
 
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.BySelector;
-import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.Until;
+import android.graphics.Point;
+import android.os.SystemClock;
+import android.view.MotionEvent;
 import android.widget.TextView;
 
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.UiObject2;
+
 /**
  * App icon, whether in all apps or in workspace/
  */
-public final class AppIcon {
-    private final Launcher mLauncher;
-    private final UiObject2 mIcon;
-
-    AppIcon(Launcher launcher, UiObject2 icon) {
-        mLauncher = launcher;
-        mIcon = icon;
+public final class AppIcon extends Launchable {
+    AppIcon(LauncherInstrumentation launcher, UiObject2 icon) {
+        super(launcher, icon);
     }
 
-    static BySelector getAppIconSelector(String appName) {
-        return By.clazz(TextView.class).text(appName).pkg(Launcher.LAUNCHER_PKG);
+    static BySelector getAppIconSelector(String appName, LauncherInstrumentation launcher) {
+        return By.clazz(TextView.class).text(appName).pkg(launcher.getLauncherPackageName());
     }
 
     /**
-     * Clicks the icon to launch its app.
+     * Long-clicks the icon to open its menu.
      */
-    public void launch() {
-        mLauncher.assertTrue("Launching an app didn't open a new window: " + mIcon.getText(),
-                mIcon.clickAndWait(Until.newWindow(), Launcher.APP_LAUNCH_TIMEOUT_MS));
-        mLauncher.assertState(Launcher.State.BACKGROUND);
+    public AppIconMenu openMenu() {
+        final Point iconCenter = mObject.getVisibleCenter();
+        final long downTime = SystemClock.uptimeMillis();
+        mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, iconCenter);
+        final UiObject2 deepShortcutsContainer = mLauncher.waitForLauncherObject(
+                "deep_shortcuts_container");
+        mLauncher.sendPointer(
+                downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, iconCenter);
+        return new AppIconMenu(mLauncher, deepShortcutsContainer);
     }
 
-    UiObject2 getIcon() {
-        return mIcon;
+    @Override
+    protected String getLongPressIndicator() {
+        return "deep_shortcuts_container";
     }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIconMenu.java b/tests/tapl/com/android/launcher3/tapl/AppIconMenu.java
new file mode 100644
index 0000000..7f28151
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/AppIconMenu.java
@@ -0,0 +1,47 @@
+/*
+ * 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.tapl;
+
+import static org.junit.Assert.assertTrue;
+
+import androidx.test.uiautomator.UiObject2;
+
+import java.util.List;
+
+/**
+ * Context menu of an app icon.
+ */
+public class AppIconMenu {
+    private final LauncherInstrumentation mLauncher;
+    private final UiObject2 mDeepShortcutsContainer;
+
+    AppIconMenu(LauncherInstrumentation launcher,
+            UiObject2 deepShortcutsContainer) {
+        mLauncher = launcher;
+        mDeepShortcutsContainer = deepShortcutsContainer;
+    }
+
+    /**
+     * Returns a menu item with a given number. Fails if it doesn't exist.
+     */
+    public AppIconMenuItem getMenuItem(int itemNumber) {
+        final List<UiObject2> menuItems = mLauncher.getObjectsInContainer(mDeepShortcutsContainer,
+                "bubble_text");
+        assertTrue(menuItems.size() > itemNumber);
+        return new AppIconMenuItem(mLauncher, menuItems.get(itemNumber));
+    }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java b/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
new file mode 100644
index 0000000..ba9c10e
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
@@ -0,0 +1,40 @@
+/*
+ * 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.tapl;
+
+import androidx.test.uiautomator.UiObject2;
+
+/**
+ * Menu item in an app icon menu.
+ */
+public class AppIconMenuItem extends Launchable {
+    AppIconMenuItem(LauncherInstrumentation launcher, UiObject2 shortcut) {
+        super(launcher, shortcut);
+    }
+
+    /**
+     * Returns the visible text of the menu item.
+     */
+    public String getText() {
+        return mObject.getText();
+    }
+
+    @Override
+    protected String getLongPressIndicator() {
+        return "drop_target_bar";
+    }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
new file mode 100644
index 0000000..c9eaf27
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -0,0 +1,129 @@
+/*
+ * 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.tapl;
+
+import static com.android.launcher3.testing.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
+
+import android.graphics.Point;
+import android.os.SystemClock;
+import android.view.MotionEvent;
+
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.testing.TestProtocol;
+
+/**
+ * Indicates the base state with a UI other than Overview running as foreground. It can also
+ * indicate Launcher as long as Launcher is not in Overview state.
+ */
+public class Background extends LauncherInstrumentation.VisibleContainer {
+    private static final int ZERO_BUTTON_SWIPE_UP_GESTURE_DURATION = 500;
+
+    Background(LauncherInstrumentation launcher) {
+        super(launcher);
+    }
+
+    @Override
+    protected LauncherInstrumentation.ContainerType getContainerType() {
+        return LauncherInstrumentation.ContainerType.BACKGROUND;
+    }
+
+    /**
+     * Swipes up or presses the square button to switch to Overview.
+     * Returns the base overview, which can be either in Launcher or the fallback recents.
+     *
+     * @return the Overview panel object.
+     */
+    @NonNull
+    public BaseOverview switchToOverview() {
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to switch from background to overview")) {
+            verifyActiveContainer();
+            goToOverviewUnchecked(BACKGROUND_APP_STATE_ORDINAL);
+            return new BaseOverview(mLauncher);
+        }
+    }
+
+    protected void goToOverviewUnchecked(int expectedState) {
+        mLauncher.getTestInfo(TestProtocol.REQUEST_ENABLE_DEBUG_TRACING);
+        switch (mLauncher.getNavigationModel()) {
+            case ZERO_BUTTON: {
+                final int centerX = mLauncher.getDevice().getDisplayWidth() / 2;
+                final int startY = getSwipeStartY();
+                final int swipeHeight = mLauncher.getTestInfo(getSwipeHeightRequestName()).
+                        getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+                final Point start = new Point(centerX, startY);
+                final Point end =
+                        new Point(centerX, startY - swipeHeight - mLauncher.getTouchSlop());
+
+                final long downTime = SystemClock.uptimeMillis();
+                mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, start);
+                mLauncher.executeAndWaitForEvent(
+                        () -> mLauncher.movePointer(
+                                downTime,
+                                downTime,
+                                ZERO_BUTTON_SWIPE_UP_GESTURE_DURATION,
+                                start,
+                                end),
+                        event -> TestProtocol.PAUSE_DETECTED_MESSAGE.equals(event.getClassName()),
+                        "Pause wasn't detected");
+                mLauncher.sendPointer(
+                        downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, end);
+                break;
+            }
+
+            case TWO_BUTTON: {
+                final int startX;
+                final int startY;
+                final int endX;
+                final int endY;
+                final int swipeLength = mLauncher.getTestInfo(getSwipeHeightRequestName()).
+                        getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD) + mLauncher.getTouchSlop();
+
+                if (mLauncher.getDevice().isNaturalOrientation()) {
+                    startX = endX = mLauncher.getDevice().getDisplayWidth() / 2;
+                    startY = getSwipeStartY();
+                    endY = startY - swipeLength;
+                } else {
+                    startX = getSwipeStartX();
+                    endX = startX - swipeLength;
+                    startY = endY = mLauncher.getDevice().getDisplayHeight() / 2;
+                }
+
+                mLauncher.swipeToState(startX, startY, endX, endY, 10, expectedState);
+                break;
+            }
+
+            case THREE_BUTTON:
+                mLauncher.waitForSystemUiObject("recent_apps").click();
+                break;
+        }
+        mLauncher.getTestInfo(TestProtocol.REQUEST_DISABLE_DEBUG_TRACING);
+    }
+
+    protected String getSwipeHeightRequestName() {
+        return TestProtocol.REQUEST_BACKGROUND_TO_OVERVIEW_SWIPE_HEIGHT;
+    }
+
+    protected int getSwipeStartX() {
+        return mLauncher.getRealDisplaySize().x - 1;
+    }
+
+    protected int getSwipeStartY() {
+        return mLauncher.getRealDisplaySize().y - 1;
+    }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
new file mode 100644
index 0000000..ace49e9
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -0,0 +1,120 @@
+/*
+ * 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.tapl;
+
+import androidx.annotation.NonNull;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.Direction;
+import androidx.test.uiautomator.UiObject2;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Common overview pane for both Launcher and fallback recents
+ */
+public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
+    private static final int FLING_SPEED = LauncherInstrumentation.isAvd() ? 500 : 1500;
+    private static final int FLINGS_FOR_DISMISS_LIMIT = 40;
+
+    BaseOverview(LauncherInstrumentation launcher) {
+        super(launcher);
+        verifyActiveContainer();
+    }
+
+    @Override
+    protected LauncherInstrumentation.ContainerType getContainerType() {
+        return LauncherInstrumentation.ContainerType.BASE_OVERVIEW;
+    }
+
+    /**
+     * Flings forward (left) and waits the fling's end.
+     */
+    public void flingForward() {
+        try (LauncherInstrumentation.Closable c =
+                     mLauncher.addContextLayer("want to fling forward in overview")) {
+            LauncherInstrumentation.log("Overview.flingForward before fling");
+            final UiObject2 overview = verifyActiveContainer();
+            overview.setGestureMargins(mLauncher.getEdgeSensitivityWidth(), 0, 0, 0);
+            overview.fling(Direction.LEFT, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
+            mLauncher.waitForIdle();
+            verifyActiveContainer();
+        }
+    }
+
+    /**
+     * Dismissed all tasks by scrolling to Clear-all button and pressing it.
+     */
+    public Workspace dismissAllTasks() {
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "dismissing all tasks")) {
+            final BySelector clearAllSelector = mLauncher.getLauncherObjectSelector("clear_all");
+            for (int i = 0;
+                    i < FLINGS_FOR_DISMISS_LIMIT
+                            && !verifyActiveContainer().hasObject(clearAllSelector);
+                    ++i) {
+                flingForward();
+            }
+
+            mLauncher.getObjectInContainer(verifyActiveContainer(), clearAllSelector).click();
+            try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+                    "dismissed all tasks")) {
+                return new Workspace(mLauncher);
+            }
+        }
+    }
+
+    /**
+     * Flings backward (right) and waits the fling's end.
+     */
+    public void flingBackward() {
+        try (LauncherInstrumentation.Closable c =
+                     mLauncher.addContextLayer("want to fling backward in overview")) {
+            LauncherInstrumentation.log("Overview.flingBackward before fling");
+            final UiObject2 overview = verifyActiveContainer();
+            overview.setGestureMargins(0, 0, mLauncher.getEdgeSensitivityWidth(), 0);
+            overview.fling(Direction.RIGHT, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
+            mLauncher.waitForIdle();
+            verifyActiveContainer();
+        }
+    }
+
+    /**
+     * Gets the current task in the carousel, or fails if the carousel is empty.
+     *
+     * @return the task in the middle of the visible tasks list.
+     */
+    @NonNull
+    public OverviewTask getCurrentTask() {
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to get current task")) {
+            verifyActiveContainer();
+            final List<UiObject2> taskViews = mLauncher.getDevice().findObjects(
+                    mLauncher.getLauncherObjectSelector("snapshot"));
+            mLauncher.assertNotEquals("Unable to find a task", 0, taskViews.size());
+
+            // taskViews contains up to 3 task views: the 'main' (having the widest visible
+            // part) one in the center, and parts of its right and left siblings. Find the
+            // main task view by its width.
+            final UiObject2 widestTask = Collections.max(taskViews,
+                    (t1, t2) -> Integer.compare(t1.getVisibleBounds().width(),
+                            t2.getVisibleBounds().width()));
+
+            return new OverviewTask(mLauncher, widestTask, this);
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/Folder.java b/tests/tapl/com/android/launcher3/tapl/Folder.java
new file mode 100644
index 0000000..6e6734d
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/Folder.java
@@ -0,0 +1,35 @@
+/*
+ * 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.tapl;
+
+import android.widget.FrameLayout;
+
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.UiObject2;
+
+/**
+ * App folder in workspace/
+ */
+public final class Folder {
+    Folder(LauncherInstrumentation launcher, UiObject2 icon) {
+    }
+
+    static BySelector getSelector(String folderName, LauncherInstrumentation launcher) {
+        return By.clazz(FrameLayout.class).desc(folderName).pkg(launcher.getLauncherPackageName());
+    }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/Home.java b/tests/tapl/com/android/launcher3/tapl/Home.java
index 0ec1a64..cfc4374 100644
--- a/tests/tapl/com/android/launcher3/tapl/Home.java
+++ b/tests/tapl/com/android/launcher3/tapl/Home.java
@@ -16,38 +16,28 @@
 
 package com.android.launcher3.tapl;
 
-import static junit.framework.TestCase.assertTrue;
+import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
 
-import android.graphics.Point;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.test.uiautomator.Direction;
-import android.support.test.uiautomator.UiObject2;
-import android.view.KeyEvent;
+import androidx.annotation.NonNull;
 
 /**
  * Operations on the home screen.
+ *
+ * Launcher can be invoked both when its activity is in the foreground and when it is in the
+ * background. This class is a parent of the two classes {@link Background} and {@link Workspace}
+ * that essentially represents these two activity states. Any gestures (e.g., switchToOverview) that
+ * can be performed in both of these states can be defined here.
  */
-public final class Home {
+public abstract class Home extends Background {
 
-    private final Launcher mLauncher;
-    private final UiObject2 mHotseat;
-    private final int ICON_DRAG_SPEED = 2000;
-
-    Home(Launcher launcher) {
-        mLauncher = launcher;
-        assertState();
-        mHotseat = launcher.waitForLauncherObject("hotseat");
+    protected Home(LauncherInstrumentation launcher) {
+        super(launcher);
+        verifyActiveContainer();
     }
 
-    /**
-     * Asserts that we are in home.
-     *
-     * @return Workspace.
-     */
-    @NonNull
-    private UiObject2 assertState() {
-        return mLauncher.assertState(Launcher.State.HOME);
+    @Override
+    protected LauncherInstrumentation.ContainerType getContainerType() {
+        return LauncherInstrumentation.ContainerType.WORKSPACE;
     }
 
     /**
@@ -56,132 +46,16 @@
      * @return the Overview panel object.
      */
     @NonNull
+    @Override
     public Overview switchToOverview() {
-        assertState();
-        if (mLauncher.isSwipeUpEnabled()) {
-            final int height = mLauncher.getDevice().getDisplayHeight();
-            final UiObject2 navBar = mLauncher.getSystemUiObject("navigation_bar_frame");
-
-            // Swipe from nav bar to 2/3rd down the screen.
-            mLauncher.swipe(
-                    navBar.getVisibleBounds().centerX(), navBar.getVisibleBounds().centerY(),
-                    navBar.getVisibleBounds().centerX(), height * 2 / 3,
-                    (navBar.getVisibleBounds().centerY() - height * 2 / 3) / 100); // 100 px/step
-        } else {
-            mLauncher.getSystemUiObject("recent_apps").click();
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to switch from home to overview")) {
+            verifyActiveContainer();
+            goToOverviewUnchecked(OVERVIEW_STATE_ORDINAL);
+            try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+                    "performed the switch action")) {
+                return new Overview(mLauncher);
+            }
         }
-
-        return new Overview(mLauncher);
-    }
-
-    /**
-     * Swipes up to All Apps.
-     *
-     * @return the App Apps object.
-     */
-    @NonNull
-    public AllAppsFromHome switchToAllApps() {
-        assertState();
-        if (mLauncher.isSwipeUpEnabled()) {
-            int midX = mLauncher.getDevice().getDisplayWidth() / 2;
-            int height = mLauncher.getDevice().getDisplayHeight();
-            // Swipe from 6/7ths down the screen to 1/7th down the screen.
-            mLauncher.swipe(
-                    midX,
-                    height * 6 / 7,
-                    midX,
-                    height / 7,
-                    (height * 2 / 3) / 100); // 100 px/step
-        } else {
-            // Swipe from the hotseat to near the top, e.g. 10% of the screen.
-            final UiObject2 hotseat = mHotseat;
-            final Point start = hotseat.getVisibleCenter();
-            final int endY = (int) (mLauncher.getDevice().getDisplayHeight() * 0.1f);
-            mLauncher.swipe(
-                    start.x,
-                    start.y,
-                    start.x,
-                    endY,
-                    (start.y - endY) / 100); // 100 px/step
-        }
-
-        return new AllAppsFromHome(mLauncher);
-    }
-
-    /**
-     * Returns an icon for the app, if currently visible.
-     *
-     * @param appName name of the app
-     * @return app icon, if found, null otherwise.
-     */
-    @Nullable
-    public AppIcon tryGetWorkspaceAppIcon(String appName) {
-        final UiObject2 workspace = assertState();
-        final UiObject2 icon = workspace.findObject(AppIcon.getAppIconSelector(appName));
-        return icon != null ? new AppIcon(mLauncher, icon) : null;
-    }
-
-    /**
-     * Ensures that workspace is scrollable. If it's not, drags an icon icons from hotseat to the
-     * second screen.
-     */
-    public void ensureWorkspaceIsScrollable() {
-        final UiObject2 workspace = assertState();
-        if (!isWorkspaceScrollable(workspace)) {
-            dragIconToNextScreen(getHotseatAppIcon("Messages"), workspace);
-        }
-        assertTrue("Home screen workspace didn't become scrollable",
-                isWorkspaceScrollable(workspace));
-    }
-
-    private boolean isWorkspaceScrollable(UiObject2 workspace) {
-        return workspace.isScrollable();
-    }
-
-    @NonNull
-    private AppIcon getHotseatAppIcon(String appName) {
-        return new AppIcon(mLauncher, mLauncher.getObjectInContainer(
-                mHotseat, AppIcon.getAppIconSelector(appName)));
-    }
-
-    private void dragIconToNextScreen(AppIcon app, UiObject2 workspace) {
-        final Point dest = new Point(
-                mLauncher.getDevice().getDisplayWidth(), workspace.getVisibleBounds().centerY());
-        app.getIcon().drag(dest, ICON_DRAG_SPEED);
-        assertState();
-    }
-
-    /**
-     * Flings to get to screens on the right. Waits for scrolling and a possible overscroll
-     * recoil to complete.
-     */
-    public void flingForward() {
-        final UiObject2 workspace = assertState();
-        workspace.fling(Direction.RIGHT);
-        mLauncher.waitForIdle();
-        assertState();
-    }
-
-    /**
-     * Flings to get to screens on the left.  Waits for scrolling and a possible overscroll
-     * recoil to complete.
-     */
-    public void flingBackward() {
-        final UiObject2 workspace = assertState();
-        workspace.fling(Direction.LEFT);
-        mLauncher.waitForIdle();
-        assertState();
-    }
-
-    /**
-     * Opens widgets container by pressing Ctrl+W.
-     *
-     * @return the widgets container.
-     */
-    @NonNull
-    public Widgets openAllWidgets() {
-        assertState();
-        mLauncher.getDevice().pressKeyCode(KeyEvent.KEYCODE_W, KeyEvent.META_CTRL_ON);
-        return new Widgets(mLauncher);
     }
 }
\ 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
new file mode 100644
index 0000000..d4bdafa
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java
@@ -0,0 +1,90 @@
+/*
+ * 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.tapl;
+
+import android.graphics.Point;
+
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
+
+import com.android.launcher3.testing.TestProtocol;
+
+/**
+ * Ancestor for AppIcon and AppMenuItem.
+ */
+abstract class Launchable {
+    private static final int WAIT_TIME_MS = 60000;
+    protected final LauncherInstrumentation mLauncher;
+
+    protected final UiObject2 mObject;
+
+    Launchable(LauncherInstrumentation launcher, UiObject2 object) {
+        mObject = object;
+        mLauncher = launcher;
+    }
+
+    UiObject2 getObject() {
+        return mObject;
+    }
+
+    /**
+     * Clicks the object to launch its app.
+     */
+    public Background launch(String expectedPackageName) {
+        return launch(By.pkg(expectedPackageName));
+    }
+
+    private Background launch(BySelector selector) {
+        LauncherInstrumentation.log("Launchable.launch before click " +
+                mObject.getVisibleCenter() + " in " + mObject.getVisibleBounds());
+        mLauncher.getTestInfo(TestProtocol.REQUEST_ENABLE_DEBUG_TRACING);
+        mLauncher.assertTrue(
+                "Launching an app didn't open a new window: " + mObject.getText(),
+                mObject.clickAndWait(Until.newWindow(), WAIT_TIME_MS));
+        mLauncher.getTestInfo(TestProtocol.REQUEST_DISABLE_DEBUG_TRACING);
+        mLauncher.assertTrue(
+                "App didn't start: " + selector,
+                mLauncher.getDevice().wait(Until.hasObject(selector),
+                        LauncherInstrumentation.WAIT_TIME_MS));
+        return new Background(mLauncher);
+    }
+
+    /**
+     * Drags an object to the center of homescreen.
+     */
+    public Workspace dragToWorkspace() {
+        final Point launchableCenter = getObject().getVisibleCenter();
+        final Point displaySize = mLauncher.getRealDisplaySize();
+        final int width = displaySize.x / 2;
+        Workspace.dragIconToWorkspace(
+                mLauncher,
+                this,
+                new Point(
+                        launchableCenter.x >= width ?
+                                launchableCenter.x - width / 2 : launchableCenter.x + width / 2,
+                        displaySize.y / 2),
+                getLongPressIndicator());
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "dragged launchable to workspace")) {
+            return new Workspace(mLauncher);
+        }
+    }
+
+    protected abstract String getLongPressIndicator();
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/Launcher.java b/tests/tapl/com/android/launcher3/tapl/Launcher.java
deleted file mode 100644
index 5201dc8..0000000
--- a/tests/tapl/com/android/launcher3/tapl/Launcher.java
+++ /dev/null
@@ -1,303 +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.tapl;
-
-import static com.android.systemui.shared.system.SettingsCompat.SWIPE_UP_SETTING_NAME;
-
-import android.content.res.Resources;
-import android.os.RemoteException;
-import android.provider.Settings;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.BySelector;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.Until;
-import android.util.Log;
-
-import org.junit.Assert;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
-/**
- * The main tapl object. The only object that can be explicitly constructed by the using code. It
- * produces all other objects.
- */
-public final class Launcher {
-
-    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";
-
-    enum State {HOME, ALL_APPS, OVERVIEW, WIDGETS, BACKGROUND}
-
-    static final String LAUNCHER_PKG = "com.google.android.apps.nexuslauncher";
-    static final int APP_LAUNCH_TIMEOUT_MS = 10000;
-    private static final int UI_OBJECT_WAIT_TIMEOUT_MS = 10000;
-    private static final String SWIPE_UP_SETTING_AVAILABLE_RES_NAME =
-            "config_swipe_up_gesture_setting_available";
-    private static final String SWIPE_UP_ENABLED_DEFAULT_RES_NAME =
-            "config_swipe_up_gesture_default";
-    private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
-    private static final String TAG = "tapl.Launcher";
-    private final UiDevice mDevice;
-    private final boolean mSwipeUpEnabled;
-
-    /**
-     * Constructs the root of TAPL hierarchy. You get all other object from it.
-     */
-    public Launcher(UiDevice device) {
-        mDevice = device;
-        final boolean swipeUpEnabledDefault =
-                !getSystemBooleanRes(SWIPE_UP_SETTING_AVAILABLE_RES_NAME) ||
-                        getSystemBooleanRes(SWIPE_UP_ENABLED_DEFAULT_RES_NAME);
-        mSwipeUpEnabled = Settings.Secure.getInt(
-                InstrumentationRegistry.getTargetContext().getContentResolver(),
-                SWIPE_UP_SETTING_NAME,
-                swipeUpEnabledDefault ? 1 : 0) == 1;
-    }
-
-    private boolean getSystemBooleanRes(String resName) {
-        final Resources res = Resources.getSystem();
-        final int resId = res.getIdentifier(resName, "bool", "android");
-        assertTrue("Resource not found: " + resName, resId != 0);
-        return res.getBoolean(resId);
-    }
-
-    private void dumpViewHierarchy() {
-        final ByteArrayOutputStream stream = new ByteArrayOutputStream();
-        try {
-            mDevice.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);
-        }
-    }
-
-    void fail(String message) {
-        dumpViewHierarchy();
-        Assert.fail(message);
-    }
-
-    void assertTrue(String message, boolean condition) {
-        if (!condition) {
-            fail(message);
-        }
-    }
-
-    void assertNotNull(String message, Object object) {
-        assertTrue(message, object != null);
-    }
-
-    private void failEquals(String message, Object actual) {
-        String formatted = "Values should be different. ";
-        if (message != null) {
-            formatted = message + ". ";
-        }
-
-        formatted += "Actual: " + actual;
-        fail(formatted);
-    }
-
-    void assertNotEquals(String message, int unexpected, int actual) {
-        if (unexpected == actual) {
-            failEquals(message, actual);
-        }
-    }
-
-    boolean isSwipeUpEnabled() {
-        return mSwipeUpEnabled;
-    }
-
-    UiObject2 assertState(State state) {
-        switch (state) {
-            case HOME: {
-                //waitUntilGone(APPS_RES_ID);
-                waitUntilGone(OVERVIEW_RES_ID);
-                waitUntilGone(WIDGETS_RES_ID);
-                return waitForLauncherObject(WORKSPACE_RES_ID);
-            }
-            case WIDGETS: {
-                waitUntilGone(WORKSPACE_RES_ID);
-                waitUntilGone(APPS_RES_ID);
-                waitUntilGone(OVERVIEW_RES_ID);
-                return waitForLauncherObject(WIDGETS_RES_ID);
-            }
-            case ALL_APPS: {
-                waitUntilGone(OVERVIEW_RES_ID);
-                waitUntilGone(WORKSPACE_RES_ID);
-                waitUntilGone(WIDGETS_RES_ID);
-                return waitForLauncherObject(APPS_RES_ID);
-            }
-            case OVERVIEW: {
-                //waitForLauncherObject(APPS_RES_ID);
-                waitUntilGone(WORKSPACE_RES_ID);
-                waitUntilGone(WIDGETS_RES_ID);
-                return waitForLauncherObject(OVERVIEW_RES_ID);
-            }
-            case BACKGROUND: {
-                waitUntilGone(WORKSPACE_RES_ID);
-                waitUntilGone(APPS_RES_ID);
-                waitUntilGone(OVERVIEW_RES_ID);
-                waitUntilGone(WIDGETS_RES_ID);
-                return null;
-            }
-            default:
-                fail("Invalid state: " + state);
-                return null;
-        }
-    }
-
-    /**
-     * Presses nav bar home button.
-     *
-     * @return the Home object.
-     */
-    public Home pressHome() {
-        getSystemUiObject("home").click();
-        return getHome();
-    }
-
-    /**
-     * Gets the Home object if the current state is "active home", i.e. workspace. Fails if the
-     * launcher is not in that state.
-     *
-     * @return Home object.
-     */
-    @NonNull
-    public Home getHome() {
-        return new Home(this);
-    }
-
-    /**
-     * Gets the Widgets object if the current state is showing all widgets. Fails if the launcher is
-     * not in that state.
-     *
-     * @return Widgets object.
-     */
-    @NonNull
-    public Widgets getAllWidgets() {
-        return new Widgets(this);
-    }
-
-    /**
-     * Gets the Overview object if the current state is showing the overview panel. Fails if the
-     * launcher is not in that state.
-     *
-     * @return Overview object.
-     */
-    @NonNull
-    public Overview getOverview() {
-        return new Overview(this);
-    }
-
-    /**
-     * Gets the All Apps object if the current state is showing the all apps panel. Fails if the
-     * launcher is not in that state.
-     *
-     * @return All Aps object.
-     */
-    @NonNull
-    public AllAppsFromHome getAllApps() {
-        return new AllAppsFromHome(this);
-    }
-
-    /**
-     * Gets the All Apps object if the current state is showing the all apps panel. Returns null if
-     * the launcher is not in that state.
-     *
-     * @return All Aps object or null.
-     */
-    @Nullable
-    public AllAppsFromHome tryGetAllApps() {
-        return tryGetLauncherObject(APPS_RES_ID) != null ? getAllApps() : null;
-    }
-
-    private void waitUntilGone(String resId) {
-//        assertTrue("Unexpected launcher object visible: " + resId,
-//                mDevice.wait(Until.gone(getLauncherObjectSelector(resId)),
-//                        UI_OBJECT_WAIT_TIMEOUT_MS));
-    }
-
-    @NonNull
-    UiObject2 getSystemUiObject(String resId) {
-        try {
-            mDevice.wakeUp();
-        } catch (RemoteException e) {
-            fail("Failed to wake up the device: " + e);
-        }
-        final UiObject2 object = mDevice.findObject(By.res(SYSTEMUI_PACKAGE, resId));
-        assertNotNull("Can't find a systemui object with id: " + resId, object);
-        return object;
-    }
-
-    @NonNull
-    UiObject2 getObjectInContainer(UiObject2 container, BySelector selector) {
-        final UiObject2 object = container.findObject(selector);
-        assertNotNull("Can't find an object with selector: " + selector, object);
-        return object;
-    }
-
-    @Nullable
-    private UiObject2 tryGetLauncherObject(String resName) {
-        return mDevice.findObject(getLauncherObjectSelector(resName));
-    }
-
-    @NonNull
-    UiObject2 waitForObjectInContainer(UiObject2 container, String resName) {
-        final UiObject2 object = container.wait(
-                Until.findObject(getLauncherObjectSelector(resName)),
-                UI_OBJECT_WAIT_TIMEOUT_MS);
-        assertNotNull("Can find a launcher object id: " + resName + " in container: " +
-                container.getResourceName(), object);
-        return object;
-    }
-
-    @NonNull
-    UiObject2 waitForLauncherObject(String resName) {
-        final UiObject2 object = mDevice.wait(Until.findObject(getLauncherObjectSelector(resName)),
-                UI_OBJECT_WAIT_TIMEOUT_MS);
-        assertNotNull("Can find a launcher object; id: " + resName, object);
-        return object;
-    }
-
-    static BySelector getLauncherObjectSelector(String resName) {
-        return By.res(LAUNCHER_PKG, resName);
-    }
-
-    @NonNull
-    UiDevice getDevice() {
-        return mDevice;
-    }
-
-    void swipe(int startX, int startY, int endX, int endY, int steps) {
-        mDevice.swipe(startX, startY, endX, endY, steps);
-        waitForIdle();
-    }
-
-    void waitForIdle() {
-        mDevice.waitForIdle();
-    }
-}
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
new file mode 100644
index 0000000..a7e6336
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -0,0 +1,829 @@
+/*
+ * 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.tapl;
+
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import static android.content.pm.PackageManager.DONT_KILL_APP;
+import static android.content.pm.PackageManager.MATCH_ALL;
+import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
+
+import static com.android.launcher3.tapl.TestHelpers.getOverviewPackageName;
+import static com.android.launcher3.testing.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
+import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL;
+
+import android.app.ActivityManager;
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.ViewConfiguration;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.Configurator;
+import androidx.test.uiautomator.Direction;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
+
+import com.android.launcher3.testing.TestProtocol;
+import com.android.systemui.shared.system.QuickStepContract;
+
+import org.junit.Assert;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+import java.util.Deque;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * The main tapl object. The only object that can be explicitly constructed by the using code. It
+ * produces all other objects.
+ */
+public final class LauncherInstrumentation {
+
+    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;
+
+    // Types for launcher containers that the user is interacting with. "Background" is a
+    // pseudo-container corresponding to inactive launcher covered by another app.
+    enum ContainerType {
+        WORKSPACE, ALL_APPS, OVERVIEW, WIDGETS, BACKGROUND, BASE_OVERVIEW
+    }
+
+    public enum NavigationModel {ZERO_BUTTON, TWO_BUTTON, THREE_BUTTON}
+
+    // Base class for launcher containers.
+    static abstract class VisibleContainer {
+        protected final LauncherInstrumentation mLauncher;
+
+        protected VisibleContainer(LauncherInstrumentation launcher) {
+            mLauncher = launcher;
+            launcher.setActiveContainer(this);
+        }
+
+        protected abstract ContainerType getContainerType();
+
+        /**
+         * Asserts that the launcher is in the mode matching 'this' object.
+         *
+         * @return UI object for the container.
+         */
+        final UiObject2 verifyActiveContainer() {
+            mLauncher.assertTrue("Attempt to use a stale container",
+                    this == sActiveContainer.get());
+            return mLauncher.verifyContainerType(getContainerType());
+        }
+    }
+
+    interface Closable extends AutoCloseable {
+        void close();
+    }
+
+    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";
+    public static final int WAIT_TIME_MS = 60000;
+    private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
+
+    private static WeakReference<VisibleContainer> sActiveContainer = new WeakReference<>(null);
+
+    private final UiDevice mDevice;
+    private final Instrumentation mInstrumentation;
+    private int mExpectedRotation = Surface.ROTATION_0;
+    private final Uri mTestProviderUri;
+    private final Deque<String> mDiagnosticContext = new LinkedList<>();
+
+    /**
+     * Constructs the root of TAPL hierarchy. You get all other objects from it.
+     */
+    public LauncherInstrumentation(Instrumentation instrumentation) {
+        mInstrumentation = instrumentation;
+        mDevice = UiDevice.getInstance(instrumentation);
+
+        // Launcher should run in test harness so that custom accessibility protocol between
+        // Launcher and TAPL is enabled. In-process tests enable this protocol with a direct call
+        // into Launcher.
+        assertTrue("Device must run in a test harness",
+                TestHelpers.isInLauncherProcess() || ActivityManager.isRunningInTestHarness());
+
+        final String testPackage = getContext().getPackageName();
+        final String targetPackage = mInstrumentation.getTargetContext().getPackageName();
+
+        // 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;
+
+        String testProviderAuthority = authorityPackage + ".TestInfo";
+        mTestProviderUri = new Uri.Builder()
+                .scheme(ContentResolver.SCHEME_CONTENT)
+                .authority(testProviderAuthority)
+                .build();
+
+        try {
+            mDevice.executeShellCommand("pm grant " + testPackage +
+                    " android.permission.WRITE_SECURE_SETTINGS");
+        } catch (IOException e) {
+            fail(e.toString());
+        }
+
+
+        PackageManager pm = getContext().getPackageManager();
+        ProviderInfo pi = pm.resolveContentProvider(
+                testProviderAuthority, MATCH_ALL | MATCH_DISABLED_COMPONENTS);
+        ComponentName cn = new ComponentName(pi.packageName, pi.name);
+
+        if (pm.getComponentEnabledSetting(cn) != COMPONENT_ENABLED_STATE_ENABLED) {
+            if (TestHelpers.isInLauncherProcess()) {
+                getContext().getPackageManager().setComponentEnabledSetting(
+                        cn, COMPONENT_ENABLED_STATE_ENABLED, DONT_KILL_APP);
+            } else {
+                try {
+                    mDevice.executeShellCommand("pm enable " + cn.flattenToString());
+                } catch (IOException e) {
+                    fail(e.toString());
+                }
+            }
+        }
+    }
+
+    Context getContext() {
+        return mInstrumentation.getContext();
+    }
+
+    Bundle getTestInfo(String request) {
+        return getContext().getContentResolver().call(mTestProviderUri, request, null, null);
+    }
+
+    void setActiveContainer(VisibleContainer container) {
+        sActiveContainer = new WeakReference<>(container);
+    }
+
+    public NavigationModel getNavigationModel() {
+        final Context baseContext = mInstrumentation.getTargetContext();
+        try {
+            // Workaround, use constructed context because both the instrumentation context and the
+            // app context are not constructed with resources that take overlays into account
+            final Context ctx = baseContext.createPackageContext("android", 0);
+            for (int i = 0; i < 100; ++i) {
+                final int currentInteractionMode = getCurrentInteractionMode(ctx);
+                final NavigationModel model = getNavigationModel(currentInteractionMode);
+                log("Interaction mode = " + currentInteractionMode + " (" + model + ")");
+                if (model != null) return model;
+                Thread.sleep(100);
+            }
+            fail("Can't detect navigation mode");
+        } catch (Exception e) {
+            fail(e.toString());
+        }
+        return NavigationModel.THREE_BUTTON;
+    }
+
+    public static NavigationModel getNavigationModel(int currentInteractionMode) {
+        if (QuickStepContract.isGesturalMode(currentInteractionMode)) {
+            return NavigationModel.ZERO_BUTTON;
+        } else if (QuickStepContract.isSwipeUpMode(currentInteractionMode)) {
+            return NavigationModel.TWO_BUTTON;
+        } else if (QuickStepContract.isLegacyMode(currentInteractionMode)) {
+            return NavigationModel.THREE_BUTTON;
+        }
+        return null;
+    }
+
+    public static boolean isAvd() {
+        return Build.MODEL.contains("Cuttlefish");
+    }
+
+    static void log(String message) {
+        Log.d(TAG, message);
+    }
+
+    Closable addContextLayer(String piece) {
+        mDiagnosticContext.addLast(piece);
+        log("Added context: " + getContextDescription());
+        return () -> {
+            log("Removing context: " + getContextDescription());
+            mDiagnosticContext.removeLast();
+        };
+    }
+
+    private void dumpViewHierarchy() {
+        final ByteArrayOutputStream stream = new ByteArrayOutputStream();
+        try {
+            mDevice.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);
+        }
+    }
+
+    private void fail(String message) {
+        log("Hierarchy dump for: " + getContextDescription() + message);
+        dumpViewHierarchy();
+        Assert.fail("http://go/tapl : " + getContextDescription() + message);
+    }
+
+    private String getContextDescription() {
+        return mDiagnosticContext.isEmpty() ? "" : String.join(", ", mDiagnosticContext) + "; ";
+    }
+
+    void assertTrue(String message, boolean condition) {
+        if (!condition) {
+            fail(message);
+        }
+    }
+
+    void assertNotNull(String message, Object object) {
+        assertTrue(message, object != null);
+    }
+
+    private void failEquals(String message, Object actual) {
+        fail(message + ". " + "Actual: " + actual);
+    }
+
+    private void assertEquals(String message, int expected, int actual) {
+        if (expected != actual) {
+            fail(message + " expected: " + expected + " but was: " + actual);
+        }
+    }
+
+    private void assertEquals(String message, String expected, String actual) {
+        if (!TextUtils.equals(expected, actual)) {
+            fail(message + " expected: '" + expected + "' but was: '" + actual + "'");
+        }
+    }
+
+    void assertEquals(String message, long expected, long actual) {
+        if (expected != actual) {
+            fail(message + " expected: " + expected + " but was: " + actual);
+        }
+    }
+
+    void assertNotEquals(String message, int unexpected, int actual) {
+        if (unexpected == actual) {
+            failEquals(message, actual);
+        }
+    }
+
+    public void setExpectedRotation(int expectedRotation) {
+        mExpectedRotation = expectedRotation;
+    }
+
+    public String getNavigationModeMismatchError() {
+        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.ZERO_BUTTON) != hasHomeButton) {
+            return "Presence of home button doesn't match the interaction mode, mode="
+                    + navigationModel.name() + ", hasHome=" + hasHomeButton;
+        }
+        return null;
+    }
+
+    private UiObject2 verifyContainerType(ContainerType containerType) {
+        assertEquals("Unexpected display rotation",
+                mExpectedRotation, mDevice.getDisplayRotation());
+        final String error = getNavigationModeMismatchError();
+        assertTrue(error, error == null);
+        log("verifyContainerType: " + containerType);
+
+        try (Closable c = addContextLayer(
+                "but the current state is not " + containerType.name())) {
+            switch (containerType) {
+                case WORKSPACE: {
+                    if (mDevice.isNaturalOrientation()) {
+                        waitForLauncherObject(APPS_RES_ID);
+                    } else {
+                        waitUntilGone(APPS_RES_ID);
+                    }
+                    waitUntilGone(OVERVIEW_RES_ID);
+                    waitUntilGone(WIDGETS_RES_ID);
+                    return waitForLauncherObject(WORKSPACE_RES_ID);
+                }
+                case WIDGETS: {
+                    waitUntilGone(WORKSPACE_RES_ID);
+                    waitUntilGone(APPS_RES_ID);
+                    waitUntilGone(OVERVIEW_RES_ID);
+                    return waitForLauncherObject(WIDGETS_RES_ID);
+                }
+                case ALL_APPS: {
+                    waitUntilGone(WORKSPACE_RES_ID);
+                    waitUntilGone(OVERVIEW_RES_ID);
+                    waitUntilGone(WIDGETS_RES_ID);
+                    return waitForLauncherObject(APPS_RES_ID);
+                }
+                case OVERVIEW: {
+                    if (mDevice.isNaturalOrientation()) {
+                        waitForLauncherObject(APPS_RES_ID);
+                    } else {
+                        waitUntilGone(APPS_RES_ID);
+                    }
+                    waitUntilGone(WORKSPACE_RES_ID);
+                    waitUntilGone(WIDGETS_RES_ID);
+
+                    return waitForLauncherObject(OVERVIEW_RES_ID);
+                }
+                case BASE_OVERVIEW: {
+                    return waitForFallbackLauncherObject(OVERVIEW_RES_ID);
+                }
+                case BACKGROUND: {
+                    waitUntilGone(WORKSPACE_RES_ID);
+                    waitUntilGone(APPS_RES_ID);
+                    waitUntilGone(OVERVIEW_RES_ID);
+                    waitUntilGone(WIDGETS_RES_ID);
+                    return null;
+                }
+                default:
+                    fail("Invalid state: " + containerType);
+                    return null;
+            }
+        }
+    }
+
+    Parcelable executeAndWaitForEvent(Runnable command,
+            UiAutomation.AccessibilityEventFilter eventFilter, String message) {
+        try {
+            final AccessibilityEvent event =
+                    mInstrumentation.getUiAutomation().executeAndWaitForEvent(
+                            command, eventFilter, WAIT_TIME_MS);
+            assertNotNull("executeAndWaitForEvent returned null (this can't happen)", event);
+            return event.getParcelableData();
+        } catch (TimeoutException e) {
+            fail(message);
+            return null;
+        }
+    }
+
+    Bundle getAnswerFromLauncher(UiObject2 view, String requestTag) {
+        // Send a fake set-text request to Launcher to initiate a response with requested data.
+        final String responseTag = requestTag + TestProtocol.RESPONSE_MESSAGE_POSTFIX;
+        return (Bundle) executeAndWaitForEvent(
+                () -> view.setText(requestTag),
+                event -> responseTag.equals(event.getClassName()),
+                "Launcher didn't respond to request: " + requestTag);
+    }
+
+    /**
+     * Presses nav bar home button.
+     *
+     * @return the Workspace object.
+     */
+    public Workspace pressHome() {
+        // Click home, then wait for any accessibility event, then wait until accessibility events
+        // stop.
+        // We need waiting for any accessibility event generated after pressing Home because
+        // otherwise waitForIdle may return immediately in case when there was a big enough pause in
+        // accessibility events prior to pressing Home.
+        final String action;
+        if (getNavigationModel() == NavigationModel.ZERO_BUTTON) {
+            final Point displaySize = getRealDisplaySize();
+
+            if (hasLauncherObject("deep_shortcuts_container")) {
+                linearGesture(
+                        displaySize.x / 2, displaySize.y - 1,
+                        displaySize.x / 2, 0,
+                        ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME);
+                assertTrue("Context menu is still visible afterswiping up to home",
+                        !hasLauncherObject("deep_shortcuts_container"));
+            }
+            if (hasLauncherObject(WORKSPACE_RES_ID)) {
+                log(action = "already at home");
+            } else {
+                log(action = "swiping up to home");
+                final int finalState = mDevice.hasObject(By.pkg(getLauncherPackageName()))
+                        ? NORMAL_STATE_ORDINAL : BACKGROUND_APP_STATE_ORDINAL;
+
+                swipeToState(
+                        displaySize.x / 2, displaySize.y - 1,
+                        displaySize.x / 2, 0,
+                        ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME, finalState);
+            }
+        } else {
+            log(action = "clicking home button");
+            executeAndWaitForEvent(
+                    () -> {
+                        log("LauncherInstrumentation.pressHome before clicking");
+                        waitForSystemUiObject("home").click();
+                    },
+                    event -> true,
+                    "Pressing Home didn't produce any events");
+            mDevice.waitForIdle();
+        }
+        try (LauncherInstrumentation.Closable c = addContextLayer(
+                "performed action to switch to Home - " + action)) {
+            return getWorkspace();
+        }
+    }
+
+    /**
+     * Gets the Workspace object if the current state is "active home", i.e. workspace. Fails if the
+     * launcher is not in that state.
+     *
+     * @return Workspace object.
+     */
+    @NonNull
+    public Workspace getWorkspace() {
+        try (LauncherInstrumentation.Closable c = addContextLayer("want to get workspace object")) {
+            return new Workspace(this);
+        }
+    }
+
+    /**
+     * Gets the Workspace object if the current state is "background home", i.e. some other app is
+     * active. Fails if the launcher is not in that state.
+     *
+     * @return Background object.
+     */
+    @NonNull
+    public Background getBackground() {
+        return new Background(this);
+    }
+
+    /**
+     * Gets the Widgets object if the current state is showing all widgets. Fails if the launcher is
+     * not in that state.
+     *
+     * @return Widgets object.
+     */
+    @NonNull
+    public Widgets getAllWidgets() {
+        try (LauncherInstrumentation.Closable c = addContextLayer("want to get widgets")) {
+            return new Widgets(this);
+        }
+    }
+
+    @NonNull
+    public AddToHomeScreenPrompt getAddToHomeScreenPrompt() {
+        try (LauncherInstrumentation.Closable c = addContextLayer("want to get widget cell")) {
+            return new AddToHomeScreenPrompt(this);
+        }
+    }
+
+    /**
+     * Gets the Overview object if the current state is showing the overview panel. Fails if the
+     * launcher is not in that state.
+     *
+     * @return Overview object.
+     */
+    @NonNull
+    public Overview getOverview() {
+        try (LauncherInstrumentation.Closable c = addContextLayer("want to get overview")) {
+            return new Overview(this);
+        }
+    }
+
+    /**
+     * Gets the All Apps object if the current state is showing the all apps panel opened by swiping
+     * from workspace. Fails if the launcher is not in that state. Please don't call this method if
+     * App Apps was opened by swiping up from Overview, as it won't fail and will return an
+     * incorrect object.
+     *
+     * @return All Aps object.
+     */
+    @NonNull
+    public AllApps getAllApps() {
+        try (LauncherInstrumentation.Closable c = addContextLayer("want to get all apps object")) {
+            return new AllApps(this);
+        }
+    }
+
+    /**
+     * Gets the All Apps object if the current state is showing the all apps panel opened by swiping
+     * from overview. Fails if the launcher is not in that state. Please don't call this method if
+     * App Apps was opened by swiping up from home, as it won't fail and will return an
+     * incorrect object.
+     *
+     * @return All Aps object.
+     */
+    @NonNull
+    public AllAppsFromOverview getAllAppsFromOverview() {
+        try (LauncherInstrumentation.Closable c = addContextLayer("want to get all apps object")) {
+            return new AllAppsFromOverview(this);
+        }
+    }
+
+    void waitUntilGone(String resId) {
+        assertTrue("Unexpected launcher object visible: " + resId,
+                mDevice.wait(Until.gone(getLauncherObjectSelector(resId)),
+                        WAIT_TIME_MS));
+    }
+
+    private boolean hasSystemUiObject(String resId) {
+        return mDevice.hasObject(By.res(SYSTEMUI_PACKAGE, resId));
+    }
+
+    @NonNull
+    UiObject2 waitForSystemUiObject(String resId) {
+        final UiObject2 object = mDevice.wait(
+                Until.findObject(By.res(SYSTEMUI_PACKAGE, resId)), WAIT_TIME_MS);
+        assertNotNull("Can't find a systemui object with id: " + resId, object);
+        return object;
+    }
+
+    @NonNull
+    UiObject2 getObjectInContainer(UiObject2 container, BySelector selector) {
+        final UiObject2 object = container.findObject(selector);
+        assertNotNull("Can't find an object with selector: " + selector, object);
+        return object;
+    }
+
+    @NonNull
+    List<UiObject2> getObjectsInContainer(UiObject2 container, String resName) {
+        return container.findObjects(getLauncherObjectSelector(resName));
+    }
+
+    @NonNull
+    UiObject2 waitForObjectInContainer(UiObject2 container, String resName) {
+        final UiObject2 object = container.wait(
+                Until.findObject(getLauncherObjectSelector(resName)),
+                WAIT_TIME_MS);
+        assertNotNull("Can't find a launcher object id: " + resName + " in container: " +
+                container.getResourceName(), object);
+        return object;
+    }
+
+    @NonNull
+    UiObject2 waitForObjectInContainer(UiObject2 container, BySelector selector) {
+        final UiObject2 object = container.wait(
+                Until.findObject(selector),
+                WAIT_TIME_MS);
+        assertNotNull("Can't find a launcher object id: " + selector + " in container: " +
+                container.getResourceName(), object);
+        return object;
+    }
+
+    @Nullable
+    private boolean hasLauncherObject(String resId) {
+        return mDevice.hasObject(getLauncherObjectSelector(resId));
+    }
+
+    @NonNull
+    UiObject2 waitForLauncherObject(String resName) {
+        return waitForObjectBySelector(getLauncherObjectSelector(resName));
+    }
+
+    @NonNull
+    UiObject2 waitForLauncherObject(BySelector selector) {
+        return waitForObjectBySelector(selector.pkg(getLauncherPackageName()));
+    }
+
+    @NonNull
+    UiObject2 tryWaitForLauncherObject(BySelector selector, long timeout) {
+        return tryWaitForObjectBySelector(selector.pkg(getLauncherPackageName()), timeout);
+    }
+
+    @NonNull
+    UiObject2 waitForFallbackLauncherObject(String resName) {
+        return waitForObjectBySelector(getFallbackLauncherObjectSelector(resName));
+    }
+
+    private UiObject2 waitForObjectBySelector(BySelector selector) {
+        final UiObject2 object = mDevice.wait(Until.findObject(selector), WAIT_TIME_MS);
+        assertNotNull("Can't find a launcher object; selector: " + selector, object);
+        return object;
+    }
+
+    private UiObject2 tryWaitForObjectBySelector(BySelector selector, long timeout) {
+        return mDevice.wait(Until.findObject(selector), timeout);
+    }
+
+    BySelector getLauncherObjectSelector(String resName) {
+        return By.res(getLauncherPackageName(), resName);
+    }
+
+    BySelector getFallbackLauncherObjectSelector(String resName) {
+        return By.res(getOverviewPackageName(), resName);
+    }
+
+    String getLauncherPackageName() {
+        return mDevice.getLauncherPackageName();
+    }
+
+    @NonNull
+    public UiDevice getDevice() {
+        return mDevice;
+    }
+
+    void swipeToState(int startX, int startY, int endX, int endY, int steps, int expectedState) {
+        final Bundle parcel = (Bundle) executeAndWaitForEvent(
+                () -> linearGesture(startX, startY, endX, endY, steps),
+                event -> TestProtocol.SWITCHED_TO_STATE_MESSAGE.equals(event.getClassName()),
+                "Swipe failed to receive an event for the swipe end: " + startX + ", " + startY
+                        + ", " + endX + ", " + endY);
+        assertEquals("Swipe switched launcher to a wrong state;",
+                TestProtocol.stateOrdinalToString(expectedState),
+                TestProtocol.stateOrdinalToString(parcel.getInt(TestProtocol.STATE_FIELD)));
+    }
+
+    void scroll(UiObject2 container, Direction direction, float percent, Rect margins, int steps) {
+        final Rect rect = container.getVisibleBounds();
+        if (margins != null) {
+            rect.left += margins.left;
+            rect.top += margins.top;
+            rect.right -= margins.right;
+            rect.bottom -= margins.bottom;
+        }
+
+        final int startX;
+        final int startY;
+        final int endX;
+        final int endY;
+
+        switch (direction) {
+            case UP: {
+                startX = endX = rect.centerX();
+                final int vertCenter = rect.centerY();
+                final float halfGestureHeight = rect.height() * percent / 2.0f;
+                startY = (int) (vertCenter - halfGestureHeight);
+                endY = (int) (vertCenter + halfGestureHeight);
+            }
+            break;
+            case DOWN: {
+                startX = endX = rect.centerX();
+                final int vertCenter = rect.centerY();
+                final float halfGestureHeight = rect.height() * percent / 2.0f;
+                startY = (int) (vertCenter + halfGestureHeight);
+                endY = (int) (vertCenter - halfGestureHeight);
+            }
+            break;
+            default:
+                fail("Unsupported direction");
+                return;
+        }
+
+        executeAndWaitForEvent(
+                () -> linearGesture(startX, startY, endX, endY, steps),
+                event -> TestProtocol.SCROLL_FINISHED_MESSAGE.equals(event.getClassName()),
+                "Didn't receive a scroll end message: " + startX + ", " + startY
+                        + ", " + endX + ", " + endY);
+    }
+
+    // Inject a swipe gesture. Inject exactly 'steps' motion points, incrementing event time by a
+    // fixed interval each time.
+    void linearGesture(int startX, int startY, int endX, int endY, int steps) {
+        final long downTime = SystemClock.uptimeMillis();
+        final Point start = new Point(startX, startY);
+        final Point end = new Point(endX, endY);
+        sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, start);
+        final long endTime = movePointer(downTime, downTime, steps * GESTURE_STEP_MS, start, end);
+        sendPointer(downTime, endTime, MotionEvent.ACTION_UP, end);
+    }
+
+    void waitForIdle() {
+        mDevice.waitForIdle();
+    }
+
+    float getDisplayDensity() {
+        return mInstrumentation.getTargetContext().getResources().getDisplayMetrics().density;
+    }
+
+    int getTouchSlop() {
+        return ViewConfiguration.get(getContext()).getScaledTouchSlop();
+    }
+
+    public Resources getResources() {
+        return getContext().getResources();
+    }
+
+    private static MotionEvent getMotionEvent(long downTime, long eventTime, int action,
+            float x, float y) {
+        MotionEvent.PointerProperties properties = new MotionEvent.PointerProperties();
+        properties.id = 0;
+        properties.toolType = Configurator.getInstance().getToolType();
+
+        MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords();
+        coords.pressure = 1;
+        coords.size = 1;
+        coords.x = x;
+        coords.y = y;
+
+        return MotionEvent.obtain(downTime, eventTime, action, 1,
+                new MotionEvent.PointerProperties[]{properties},
+                new MotionEvent.PointerCoords[]{coords},
+                0, 0, 1.0f, 1.0f, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, 0);
+    }
+
+    void sendPointer(long downTime, long currentTime, int action, Point point) {
+        final MotionEvent event = getMotionEvent(downTime, currentTime, action, point.x, point.y);
+        mInstrumentation.getUiAutomation().injectInputEvent(event, true);
+        event.recycle();
+    }
+
+    long movePointer(long downTime, long startTime, long duration, Point from, Point to) {
+        final Point point = new Point();
+        long steps = duration / GESTURE_STEP_MS;
+        long currentTime = startTime;
+        for (long i = 0; i < steps; ++i) {
+            sleep(GESTURE_STEP_MS);
+
+            currentTime += GESTURE_STEP_MS;
+            final float progress = (currentTime - startTime) / (float) duration;
+
+            point.x = from.x + (int) (progress * (to.x - from.x));
+            point.y = from.y + (int) (progress * (to.y - from.y));
+
+            sendPointer(downTime, currentTime, MotionEvent.ACTION_MOVE, point);
+        }
+        return currentTime;
+    }
+
+    public static int getCurrentInteractionMode(Context context) {
+        return getSystemIntegerRes(context, "config_navBarInteractionMode");
+    }
+
+    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;
+        }
+    }
+
+    private static int getSystemDimensionResId(Context context, String resName) {
+        Resources res = context.getResources();
+        int resId = res.getIdentifier(resName, "dimen", "android");
+
+        if (resId != 0) {
+            return resId;
+        } else {
+            Log.e(TAG, "Failed to get system resource ID. Incompatible framework version?");
+            return -1;
+        }
+    }
+
+    static void sleep(int duration) {
+        SystemClock.sleep(duration);
+    }
+
+    int getEdgeSensitivityWidth() {
+        try {
+            final Context context = mInstrumentation.getTargetContext().createPackageContext(
+                    "android", 0);
+            return context.getResources().getDimensionPixelSize(
+                    getSystemDimensionResId(context, "config_backGestureInset")) + 1;
+        } catch (PackageManager.NameNotFoundException e) {
+            fail("Can't get edge sensitivity: " + e);
+            return 0;
+        }
+    }
+
+    Point getRealDisplaySize() {
+        final Point size = new Point();
+        getContext().getSystemService(WindowManager.class).getDefaultDisplay().getRealSize(size);
+        return size;
+    }
+}
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/Overview.java b/tests/tapl/com/android/launcher3/tapl/Overview.java
index 2251655..058831f 100644
--- a/tests/tapl/com/android/launcher3/tapl/Overview.java
+++ b/tests/tapl/com/android/launcher3/tapl/Overview.java
@@ -16,77 +16,26 @@
 
 package com.android.launcher3.tapl;
 
-import android.graphics.Point;
-import android.support.annotation.NonNull;
-import android.support.test.uiautomator.Direction;
-import android.support.test.uiautomator.UiObject2;
+import static com.android.launcher3.testing.TestProtocol.ALL_APPS_STATE_ORDINAL;
 
-import java.util.Collections;
-import java.util.List;
+import androidx.annotation.NonNull;
+import androidx.test.uiautomator.UiObject2;
+
+import com.android.launcher3.tapl.LauncherInstrumentation.ContainerType;
 
 /**
  * Overview pane.
  */
-public final class Overview {
-    private static final int DEFAULT_FLING_SPEED = 15000;
+public final class Overview extends BaseOverview {
 
-    private final Launcher mLauncher;
-
-    Overview(Launcher launcher) {
-        mLauncher = launcher;
-        assertState();
+    Overview(LauncherInstrumentation launcher) {
+        super(launcher);
+        verifyActiveContainer();
     }
 
-    /**
-     * Asserts that we are in overview.
-     *
-     * @return Overview panel.
-     */
-    @NonNull
-    private UiObject2 assertState() {
-        return mLauncher.assertState(Launcher.State.OVERVIEW);
-    }
-
-    /**
-     * Flings forward (left) and waits the fling's end.
-     */
-    public void flingForward() {
-        final UiObject2 overview = assertState();
-        overview.fling(Direction.LEFT, DEFAULT_FLING_SPEED);
-        mLauncher.waitForIdle();
-        assertState();
-    }
-
-    /**
-     * Flings backward (right) and waits the fling's end.
-     */
-    public void flingBackward() {
-        final UiObject2 overview = assertState();
-        overview.fling(Direction.RIGHT, DEFAULT_FLING_SPEED);
-        mLauncher.waitForIdle();
-        assertState();
-    }
-
-    /**
-     * Gets the current task in the carousel, or fails if the carousel is empty.
-     *
-     * @return the task in the middle of the visible tasks list.
-     */
-    @NonNull
-    public OverviewTask getCurrentTask() {
-        assertState();
-        final List<UiObject2> taskViews = mLauncher.getDevice().findObjects(
-                Launcher.getLauncherObjectSelector("snapshot"));
-        mLauncher.assertNotEquals("Unable to find a task", 0, taskViews.size());
-
-        // taskViews contains up to 3 task views: the 'main' (having the widest visible
-        // part) one in the center, and parts of its right and left siblings. Find the
-        // main task view by its width.
-        final UiObject2 widestTask = Collections.max(taskViews,
-                (t1, t2) -> Integer.compare(t1.getVisibleBounds().width(),
-                        t2.getVisibleBounds().width()));
-
-        return new OverviewTask(mLauncher, widestTask);
+    @Override
+    protected ContainerType getContainerType() {
+        return LauncherInstrumentation.ContainerType.OVERVIEW;
     }
 
     /**
@@ -96,16 +45,22 @@
      */
     @NonNull
     public AllAppsFromOverview switchToAllApps() {
-        assertState();
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to switch from overview to all apps")) {
+            verifyActiveContainer();
 
-        // Swipe from the hotseat to near the top, e.g. 10% of the screen.
-        final UiObject2 predictionRow = mLauncher.waitForLauncherObject(
-                "prediction_row");
-        final Point start = predictionRow.getVisibleCenter();
-        final int endY = (int) (mLauncher.getDevice().getDisplayHeight() * 0.1f);
-        mLauncher.swipe(
-                start.x, start.y, start.x, endY, (start.y - endY) / 100); // 100 px/step
+            // Swipe from an app icon to the top.
+            LauncherInstrumentation.log("Overview.switchToAllApps before swipe");
+            final UiObject2 allApps = mLauncher.waitForLauncherObject("apps_view");
+            mLauncher.swipeToState(mLauncher.getDevice().getDisplayWidth() / 2,
+                    allApps.getVisibleBounds().top,
+                    mLauncher.getDevice().getDisplayWidth() / 2,
+                    0, 50, ALL_APPS_STATE_ORDINAL);
 
-        return new AllAppsFromOverview(mLauncher);
+            try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+                    "swiped all way up from overview")) {
+                return new AllAppsFromOverview(mLauncher);
+            }
+        }
     }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index 68d3082..641c413 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -16,50 +16,62 @@
 
 package com.android.launcher3.tapl;
 
-import android.support.test.uiautomator.Direction;
-import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.Until;
+import android.graphics.Rect;
+
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
+
+import com.android.launcher3.testing.TestProtocol;
 
 /**
  * A recent task in the overview panel carousel.
  */
 public final class OverviewTask {
-    private final Launcher mLauncher;
+    private static final long WAIT_TIME_MS = 60000;
+    private final LauncherInstrumentation mLauncher;
     private final UiObject2 mTask;
+    private final BaseOverview mOverview;
 
-    OverviewTask(Launcher launcher, UiObject2 task) {
+    OverviewTask(LauncherInstrumentation launcher, UiObject2 task, BaseOverview overview) {
         mLauncher = launcher;
-        assertState();
         mTask = task;
+        mOverview = overview;
+        verifyActiveContainer();
     }
 
-    /**
-     * Asserts that we are in overview.
-     *
-     * @return Overview panel.
-     */
-    private void assertState() {
-        mLauncher.assertState(Launcher.State.OVERVIEW);
+    private void verifyActiveContainer() {
+        mOverview.verifyActiveContainer();
     }
 
     /**
      * Swipes the task up.
      */
     public void dismiss() {
-        assertState();
-        // Dismiss the task via flinging it up.
-        mTask.fling(Direction.DOWN);
-        mLauncher.waitForIdle();
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to dismiss a task")) {
+            verifyActiveContainer();
+            // Dismiss the task via flinging it up.
+            final Rect taskBounds = mTask.getVisibleBounds();
+            final int centerX = taskBounds.centerX();
+            final int centerY = taskBounds.centerY();
+            mLauncher.linearGesture(centerX, centerY, centerX, 0, 10);
+            mLauncher.waitForIdle();
+        }
     }
 
     /**
      * Clicks at the task.
      */
-    public void open() {
-        assertState();
-        mLauncher.assertTrue("Launching task didn't open a new window: " +
-                        mTask.getParent().getContentDescription(),
-                mTask.clickAndWait(Until.newWindow(), Launcher.APP_LAUNCH_TIMEOUT_MS));
-        mLauncher.assertState(Launcher.State.BACKGROUND);
+    public Background open() {
+        verifyActiveContainer();
+        mLauncher.getTestInfo(TestProtocol.REQUEST_ENABLE_DEBUG_TRACING);
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "clicking an overview task")) {
+            mLauncher.assertTrue("Launching task didn't open a new window: " +
+                            mTask.getParent().getContentDescription(),
+                    mTask.clickAndWait(Until.newWindow(), WAIT_TIME_MS));
+        }
+        mLauncher.getTestInfo(TestProtocol.REQUEST_DISABLE_DEBUG_TRACING);
+        return new Background(mLauncher);
     }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/TestHelpers.java b/tests/tapl/com/android/launcher3/tapl/TestHelpers.java
new file mode 100644
index 0000000..93554d2
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/TestHelpers.java
@@ -0,0 +1,84 @@
+/*
+ * 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.tapl;
+
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static androidx.test.InstrumentationRegistry.getTargetContext;
+
+import android.app.Instrumentation;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+
+import java.util.List;
+
+public class TestHelpers {
+
+    private static Boolean sIsInLauncherProcess;
+
+    public static boolean isInLauncherProcess() {
+        if (sIsInLauncherProcess == null) {
+            sIsInLauncherProcess = initIsInLauncherProcess();
+        }
+        return sIsInLauncherProcess;
+    }
+
+    private static boolean initIsInLauncherProcess() {
+        ActivityInfo info = getLauncherInMyProcess();
+
+        // If we are in the same process, we can instantiate the class name.
+        try {
+            Class launcherClazz = Class.forName("com.android.launcher3.Launcher");
+            return launcherClazz.isAssignableFrom(Class.forName(info.name));
+        } catch (Exception e) {
+            return false;
+        }
+    }
+
+    public static Intent getHomeIntentInPackage(Context context) {
+        return new Intent(Intent.ACTION_MAIN)
+                .addCategory(Intent.CATEGORY_HOME)
+                .setPackage(context.getPackageName())
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+    }
+
+    public static ActivityInfo getLauncherInMyProcess() {
+        Instrumentation instrumentation = getInstrumentation();
+        if (instrumentation.getTargetContext() == null) {
+            return null;
+        }
+
+        List<ResolveInfo> launchers = getTargetContext().getPackageManager()
+                .queryIntentActivities(getHomeIntentInPackage(getTargetContext()), 0);
+        if (launchers.size() != 1) {
+            return null;
+        }
+        return launchers.get(0).activityInfo;
+    }
+
+    public static String getOverviewPackageName() {
+        Resources res = Resources.getSystem();
+        int id = res.getIdentifier("config_recentsComponentName", "string", "android");
+        if (id != 0) {
+            return ComponentName.unflattenFromString(res.getString(id)).getPackageName();
+        }
+        return "com.android.systemui";
+    }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/Widget.java b/tests/tapl/com/android/launcher3/tapl/Widget.java
new file mode 100644
index 0000000..128789d
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/Widget.java
@@ -0,0 +1,24 @@
+/*
+ * 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.tapl;
+
+import androidx.test.uiautomator.UiObject2;
+
+public class Widget {
+    Widget(LauncherInstrumentation launcher, UiObject2 widget) {
+    }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index 7a5198a..94003be 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -16,50 +16,63 @@
 
 package com.android.launcher3.tapl;
 
-import android.support.annotation.NonNull;
-import android.support.test.uiautomator.Direction;
-import android.support.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Direction;
+import androidx.test.uiautomator.UiObject2;
+
+import com.android.launcher3.ResourceUtils;
 
 /**
  * All widgets container.
  */
-public final class Widgets {
-    private static final int FLING_SPEED = 12000;
+public final class Widgets extends LauncherInstrumentation.VisibleContainer {
+    private static final int FLING_SPEED = 1500;
 
-    private final Launcher mLauncher;
-
-    Widgets(Launcher launcher) {
-        mLauncher = launcher;
-        assertState();
+    Widgets(LauncherInstrumentation launcher) {
+        super(launcher);
+        verifyActiveContainer();
     }
 
     /**
      * Flings forward (down) and waits the fling's end.
      */
     public void flingForward() {
-        final UiObject2 widgetsContainer = assertState();
-        widgetsContainer.fling(Direction.DOWN, FLING_SPEED);
-        mLauncher.waitForIdle();
-        assertState();
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to fling forward in widgets")) {
+            LauncherInstrumentation.log("Widgets.flingForward enter");
+            final UiObject2 widgetsContainer = verifyActiveContainer();
+            widgetsContainer.setGestureMargins(0, 0, 0,
+                    ResourceUtils.getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE,
+                            mLauncher.getResources()) + 1);
+            widgetsContainer.fling(Direction.DOWN,
+                    (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
+            try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung forward")) {
+                verifyActiveContainer();
+            }
+            LauncherInstrumentation.log("Widgets.flingForward exit");
+        }
     }
 
     /**
      * Flings backward (up) and waits the fling's end.
      */
     public void flingBackward() {
-        final UiObject2 widgetsContainer = assertState();
-        widgetsContainer.fling(Direction.UP, FLING_SPEED);
-        mLauncher.waitForIdle();
-        assertState();
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to fling backwards in widgets")) {
+            LauncherInstrumentation.log("Widgets.flingBackward enter");
+            final UiObject2 widgetsContainer = verifyActiveContainer();
+            widgetsContainer.setGestureMargin(100);
+            widgetsContainer.fling(Direction.UP,
+                    (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
+            mLauncher.waitForIdle();
+            try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung back")) {
+                verifyActiveContainer();
+            }
+            LauncherInstrumentation.log("Widgets.flingBackward exit");
+        }
     }
 
-    /**
-     * Asserts that we are in widgets.
-     *
-     * @return Widgets container.
-     */
-    @NonNull
-    private UiObject2 assertState() {
-        return mLauncher.assertState(Launcher.State.WIDGETS);
+    @Override
+    protected LauncherInstrumentation.ContainerType getContainerType() {
+        return LauncherInstrumentation.ContainerType.WIDGETS;
     }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
new file mode 100644
index 0000000..b01b6f3
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -0,0 +1,240 @@
+/*
+ * 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.tapl;
+
+import static com.android.launcher3.testing.TestProtocol.ALL_APPS_STATE_ORDINAL;
+
+import static junit.framework.TestCase.assertTrue;
+
+import android.graphics.Point;
+import android.os.SystemClock;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.Direction;
+import androidx.test.uiautomator.UiObject2;
+
+import com.android.launcher3.testing.TestProtocol;
+
+/**
+ * Operations on the workspace screen.
+ */
+public final class Workspace extends Home {
+    private static final float FLING_SPEED =
+            LauncherInstrumentation.isAvd() ? 1500.0F : 3500.0F;
+    private static final int DRAG_DURACTION = 2000;
+    private final UiObject2 mHotseat;
+
+    Workspace(LauncherInstrumentation launcher) {
+        super(launcher);
+        mHotseat = launcher.waitForLauncherObject("hotseat");
+    }
+
+    /**
+     * Swipes up to All Apps.
+     *
+     * @return the App Apps object.
+     */
+    @NonNull
+    public AllApps switchToAllApps() {
+        try (LauncherInstrumentation.Closable c =
+                     mLauncher.addContextLayer("want to switch from workspace to all apps")) {
+            verifyActiveContainer();
+            final UiObject2 hotseat = mHotseat;
+            final Point start = hotseat.getVisibleCenter();
+            start.y = hotseat.getVisibleBounds().bottom - 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 = "
+                            + mLauncher.getTouchSlop());
+
+            mLauncher.getTestInfo(TestProtocol.REQUEST_ENABLE_DEBUG_TRACING);
+            mLauncher.swipeToState(
+                    start.x,
+                    start.y,
+                    start.x,
+                    start.y - swipeHeight - mLauncher.getTouchSlop(),
+                    60,
+                    ALL_APPS_STATE_ORDINAL);
+            mLauncher.getTestInfo(TestProtocol.REQUEST_DISABLE_DEBUG_TRACING);
+
+            try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+                    "swiped to all apps")) {
+                return new AllApps(mLauncher);
+            }
+        }
+    }
+
+    /**
+     * Returns an icon for the app, if currently visible.
+     *
+     * @param appName name of the app
+     * @return app icon, if found, null otherwise.
+     */
+    @Nullable
+    public AppIcon tryGetWorkspaceAppIcon(String appName) {
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to get a workspace icon")) {
+            final UiObject2 workspace = verifyActiveContainer();
+            final UiObject2 icon = workspace.findObject(
+                    AppIcon.getAppIconSelector(appName, mLauncher));
+            return icon != null ? new AppIcon(mLauncher, icon) : null;
+        }
+    }
+
+
+    /**
+     * Returns an icon for the app; fails if the icon doesn't exist.
+     *
+     * @param appName name of the app
+     * @return app icon.
+     */
+    @NonNull
+    public AppIcon getWorkspaceAppIcon(String appName) {
+        return new AppIcon(mLauncher,
+                mLauncher.getObjectInContainer(
+                        verifyActiveContainer(),
+                        AppIcon.getAppIconSelector(appName, mLauncher)));
+    }
+
+    /**
+     * Ensures that workspace is scrollable. If it's not, drags an icon icons from hotseat to the
+     * second screen.
+     */
+    public void ensureWorkspaceIsScrollable() {
+        final UiObject2 workspace = verifyActiveContainer();
+        if (!isWorkspaceScrollable(workspace)) {
+            try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                    "dragging icon to a second page of workspace to make it scrollable")) {
+                dragIconToWorkspace(
+                        mLauncher,
+                        getHotseatAppIcon("Chrome"),
+                        new Point(mLauncher.getDevice().getDisplayWidth(),
+                                workspace.getVisibleBounds().centerY()),
+                        "deep_shortcuts_container");
+                verifyActiveContainer();
+            }
+        }
+        assertTrue("Home screen workspace didn't become scrollable",
+                isWorkspaceScrollable(workspace));
+    }
+
+    private boolean isWorkspaceScrollable(UiObject2 workspace) {
+        return workspace.isScrollable();
+    }
+
+    @NonNull
+    public AppIcon getHotseatAppIcon(String appName) {
+        return new AppIcon(mLauncher, mLauncher.getObjectInContainer(
+                mHotseat, AppIcon.getAppIconSelector(appName, mLauncher)));
+    }
+
+    @NonNull
+    public Folder getHotseatFolder(String appName) {
+        return new Folder(mLauncher, mLauncher.getObjectInContainer(
+                mHotseat, Folder.getSelector(appName, mLauncher)));
+    }
+
+    static void dragIconToWorkspace(
+            LauncherInstrumentation launcher, Launchable launchable, Point dest,
+            String longPressIndicator) {
+        launcher.getTestInfo(TestProtocol.REQUEST_ENABLE_DEBUG_TRACING);
+        LauncherInstrumentation.log("dragIconToWorkspace: begin");
+        final Point launchableCenter = launchable.getObject().getVisibleCenter();
+        final long downTime = SystemClock.uptimeMillis();
+        launcher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, launchableCenter);
+        LauncherInstrumentation.log("dragIconToWorkspace: sent down");
+        launcher.waitForLauncherObject(longPressIndicator);
+        LauncherInstrumentation.log("dragIconToWorkspace: indicator");
+        launcher.movePointer(
+                downTime, SystemClock.uptimeMillis(), DRAG_DURACTION, launchableCenter, dest);
+        LauncherInstrumentation.log("dragIconToWorkspace: moved pointer");
+        launcher.sendPointer(
+                downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, dest);
+        LauncherInstrumentation.log("dragIconToWorkspace: end");
+        launcher.waitUntilGone("drop_target_bar");
+        launcher.getTestInfo(TestProtocol.REQUEST_DISABLE_DEBUG_TRACING);
+    }
+
+    /**
+     * Flings to get to screens on the right. Waits for scrolling and a possible overscroll
+     * recoil to complete.
+     */
+    public void flingForward() {
+        final UiObject2 workspace = verifyActiveContainer();
+        workspace.setGestureMargins(0, 0, mLauncher.getEdgeSensitivityWidth(), 0);
+        workspace.fling(Direction.RIGHT, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
+        mLauncher.waitForIdle();
+        verifyActiveContainer();
+    }
+
+    /**
+     * Flings to get to screens on the left.  Waits for scrolling and a possible overscroll
+     * recoil to complete.
+     */
+    public void flingBackward() {
+        final UiObject2 workspace = verifyActiveContainer();
+        workspace.setGestureMargins(mLauncher.getEdgeSensitivityWidth(), 0, 0, 0);
+        workspace.fling(Direction.LEFT, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
+        mLauncher.waitForIdle();
+        verifyActiveContainer();
+    }
+
+    /**
+     * Opens widgets container by pressing Ctrl+W.
+     *
+     * @return the widgets container.
+     */
+    @NonNull
+    public Widgets openAllWidgets() {
+        verifyActiveContainer();
+        mLauncher.getDevice().pressKeyCode(KeyEvent.KEYCODE_W, KeyEvent.META_CTRL_ON);
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer("pressed Ctrl+W")) {
+            return new Widgets(mLauncher);
+        }
+    }
+
+    @Override
+    protected String getSwipeHeightRequestName() {
+        return TestProtocol.REQUEST_HOME_TO_OVERVIEW_SWIPE_HEIGHT;
+    }
+
+    @Override
+    protected int getSwipeStartY() {
+        return mLauncher.getRealDisplaySize().y - 1;
+    }
+
+    @Nullable
+    public Widget tryGetWidget(String label, long timeout) {
+        final UiObject2 widget = mLauncher.tryWaitForLauncherObject(
+                By.clazz("com.android.launcher3.widget.LauncherAppWidgetHostView").desc(label),
+                timeout);
+        return widget != null ? new Widget(mLauncher, widget) : null;
+    }
+
+    @Nullable
+    public Widget tryGetPendingWidget(long timeout) {
+        final UiObject2 widget = mLauncher.tryWaitForLauncherObject(
+                By.clazz("com.android.launcher3.widget.PendingAppWidgetHostView"), timeout);
+        return widget != null ? new Widget(mLauncher, widget) : null;
+    }
+}
\ No newline at end of file